DisplayArea树
DisplayArea树示意图
DisplayArea概念
DisplayArea树的所有节点都继承于DisplayArea,而DisplayArea继承于WindowContainer,因此每一个节点都是一个container。DisplayArea代表了屏幕的显示区域,该显示区域是从Z值来看的,即在不同的layer上创建不同的DisplayArea。如果一个DisplayArea有子元素,那么代表该DisplayArea从Z轴上还是可分的。
DisplayContent代表一块屏幕,DisplayContent继承于DisplayArea,说明其也是一块显示区域,从z值来看,该块屏幕在不同的layer层都可以显示,因此在不同的layer都可以放DisplayArea。它的子元素类型有:TaskDisplayArea、DisplayAreaGroup、Tokens(DisplayArea的内部内)、DisplayArea(用于feature)。DisplayContent的layer总共有36层(窗口类型有36个),其最多有36个DIsplayArea,而层与层之间的z值差距很大,因此每一层上又可以放很多displayArea。
而DisplayArea树构建好了之后往里面添加activity窗口:task、activityRecord和非activity窗口tokens就构成了一颗完整的树。
窗口树种的所有节点都继承于WindowContainer,代表每一个节点都是一个容器,而每一个容器中的元素类型由父类的泛型类型决定,如:RootWindowContainer继承于WindowContainer, 表明RootWindowContainer中的元素类型为DisplayContent。Task继承于WindowContainer,表明Task中的元素类型为WindowContainer,实际上可能是ActivityRecord,也可能是Task(嵌套)。Tokens继承于DisplayArea,表明其子元素类型为WindowToken。
而WindowContainer继承ConfigurationContainer,ConfigurationContainer
中提供的是和窗口配置相关的信息(例如窗口尺寸),WindowContainer
中提供的是子类作为窗口容器所具备的能力(例如增删,排序)。
为什么引入DisplayArea树?
增加屏幕放大功能,安卓能否放大分组
再增加一个新feature,,支持是否某窗口动态放大缩小显示范围,我们以Constrained和NonConstrained方式进行区分,那个时候可能组织方式又需要改成如下形式:
可以想象,未来的未来,再想添加新功能的时候,结构将越来越复杂,维护开发过程也势必变的更加艰难。因此引入DisplayArea,在ActivityRecord、WindowState之上引入DisplayArea树,支持动态调整。
增加的新功能包装成 Feature类,Feature指定对那些layer层进行增加功能,然后在这些Layer层建立对应的DisplayArea,相邻Layer且同属于同一个Displayarea的DisplayArea合并成一个DisplayArea。新的Feature在原来的Feature 下建立DisplayArea。比如Feature1是对layer 2和layer 3的窗口进行旋转,而Feature2 是对layer 3 和layer 4 的窗口进行放大,Feature1和feature2共同作用的结果是layer 2 上的窗口先进行旋转再放大,layer3上的窗口只进行旋转,layer4 的窗口只进行放大。每一个DisplayArea的mFeatureId指定了该DisplayArea的功能。
DisplayAreaGroup
再回到DisplayAreaGroup,什么是DisplayAreaGroup?为什么需要DisplayAreaGroup?从字面上解释而言,DisplayAreaGroup就是一组DisplayArea,用来表示屏幕的一部分内容(这部分由DisplayArea组成)。之所以添加DisplayAreaGroup,主要还是为了功能拓展。例如:(使用Google 2021 Bootcamp示例)
一个屏幕上想显示多个Display的内容,我们想针对某一Display做旋转要求的时候,在没有DisplayAreaGroup的存在下,很难做到Configuration正确更新,就算能做,我们也要在一致性代码里面加上很多的特殊性判断,这个根本就不符合Google manline的要求。在DisplayAreaGroup的加持下,这个要求变的就十分简单。只要针对DisplayAreaGroup做对应的Configuration更新即可。
即DisplayArea是在Z轴维度进行分割窗口层次,而DisplayAreaGroup是在平面层次分割窗口层次。
DisplayArea树产生简介
HierachyBuilder是用于产生DisplayArea树的类,传入TaskDisplayArea集合、imeContainer和RootDisplayArea,然后调用builder方法就会产生RootDisplayArea为root的DisplayArea树。android 12 引入了DisplayAreaGroup的概念,因此一般一个屏幕同时存在多棵DisplayArea树,一颗是DisplayContent为root的树,其它几棵是DisplayAreaGroup为root的树。每一颗树都是由HierachyBuilder这个类产生的,通过DisplayAreaPolicyBuilder这个类的build的方法,可以同时调用这些HierachyBuilder的build方法建树,让多棵DisplayArea树的构建变得更为简单。
如图所示,产生DisplayArea树要经过上面的过程,为了更方面的调用,可以将上面的过程包装在一个类中,这个类叫做Provider,设置不同的参数,就有不同的Provider。这些Provider都继承Provider接口,可以将具体的Provider存为配置文件,然后从Provider接口的静态方法fromResources中读取配置文件产生Provider,然后调用Provider的instantiate方法产生DisplayAreaPolicy.Result
public interface Provider {
/**
* Instantiates a new {@link DisplayAreaPolicy}. It should set up the {@link DisplayArea}
* hierarchy.
*
* @see DisplayAreaPolicy#DisplayAreaPolicy
*/
DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content,
RootDisplayArea root, DisplayArea.Tokens imeContainer);
/**
* Instantiates the device-specific {@link Provider}.
*/
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);
}
}
}
fromResources方法没有从资源中读到name指定 文件时会产生默认的Provider:DefaultProvider。DefaultProvider没有设置feature和DisplayAreaGroup集合。
相关数据结构:
DisplayAreaPolicyBuilder
HierarchyBuilder mRootHierarchyBuilder;
private final ArrayList<HierarchyBuilder> mDisplayAreaGroupHierarchyBuilders =
new ArrayList<>();
DisplayAreaPolicyBuilder中含有DisplayArea树集合的builder,包括根displayArea树的buildermRootHierarchyBuilder,和DisplayAreaGroup树的builder集合mDisplayAreaGroupHierarchyBuilders 。这些树的builder都是HierarchyBuilder 类型。最后树构建成功后会返回一个DisplayAreaPolicy类型,决定了该屏幕的窗口结构。
HierarchyBuilder
private final RootDisplayArea mRoot;
private final ArrayList<DisplayAreaPolicyBuilder.Feature> mFeatures = new ArrayList<>();
private final ArrayList<TaskDisplayArea> mTaskDisplayAreas = new ArrayList<>();
@Nullable
private DisplayArea.Tokens mImeContainer;
mRoot指定了该树的根节点,通过传入mFeatures 、mTaskDisplayAreas 和mImeContainer构建了一个含有feature节点、task container节点、ime container节点和tokens节点的DisplayArea树。
DisplayAreaPolicy
是一个抽象类,实现类为DisplayAreaPolicyBuilder.Result.
其主要就是提供了一个RooDisplayArea类型的mRoot成员,该成员就是屏幕的DisplayArea树的根节点。DisplayArea树包括一般DisplayArea树和DisplayAreaGroup的DisplayArea树。mRoot其实就是DisplayContent。
**
* Policy that manages {@link DisplayArea}.
*/
public abstract class DisplayAreaPolicy {
protected final WindowManagerService mWmService;
/**
* The {@link RootDisplayArea} of the whole logical display. All {@link DisplayArea}s must be
* (direct or indirect) descendants of this area.
*/
protected final RootDisplayArea mRoot;
/**
* Constructs a new {@link DisplayAreaPolicy}
*
* @param wmService the window manager service instance
* @param root the root display area under which the policy operates
*/
protected DisplayAreaPolicy(WindowManagerService wmService, RootDisplayArea root) {
mWmService = wmService;
mRoot = root;
}
DisplayContent和DisplayAreaGroup继承于RootDisplayArea,RootDisplayArea是DisplayArea树的根。RootDisplayArea本身继承于DisplayArea。树的产生逻辑是,通过DispolicyAreaBulder产生树,而DispolicyAreaBuilder是通过它里面的HierachyBuilder集合产生树,而HierachyBuilder又是通过传入RootDisplayArea,指定了root,传入mTaskDisplayAreas 、mImeContainer产生对应的节点和tokens节点(tokens节点是默认就会产生的)从而产生一颗DisplayArea树,如果传入的rootDisplayArea是DisplayContent则就是总的树,如果是DisplayAreaGroup则就是DisplayAreaGroup树。
具体分析
一 RootWindowContainer产生过程
SystemServer起来后,启动WMS时,在WindowManagerService的构造函数中创建RootWindowContainer实例,并赋值给WMS.mRoot。
调用链:SystemServer.main() -> run() -> startOtherServices() -> WindowManagerService.main() -> new WindowManagerService()
# WindowManagerService.WindowManagerService()
mRoot = new RootWindowContainer(this);
紧接着在建立AMS与WMS的关联的过程中,将mRoot传递到ATMS中,赋值给ATMS.mRootWindowContainer。
调用链:startOtherServices() -> ActivityManagerService.setWindowManager(wm) -> ActivityTaskManagerService.setWindowManager()
# ActivityTaskManagerService
public void setWindowManager(WindowManagerService wm) {
synchronized (mGlobalLock) {
mWindowManager = wm;
// 赋值给ATMS.mRootWindowContainer
mRootWindowContainer = wm.mRoot;
mTempConfig.setToDefaults();
mTempConfig.setLocales(LocaleList.getDefault());
mConfigurationSeq = mTempConfig.seq = 1;
mRootWindowContainer.onConfigurationChanged(mTempConfig);
mLockTaskController.setWindowManager(wm);
mStackSupervisor.setWindowManager(wm);
// 接下来初始化DisplayContent
mRootWindowContainer.setWindowManager(wm);
}
}
2、创建DisplayContent
从DisplayManagerService中获取屏幕集合,然后为每个屏幕创建DisplayContent
void setWindowManager(WindowManagerService wm) {
mWindowManager = wm;
mDisplayManager = mService.mContext.getSystemService(DisplayManager.class);
mDisplayManager.registerDisplayListener(this, mService.mUiHandler);
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
final Display[] displays = mDisplayManager.getDisplays();
for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {
final Display display = displays[displayNdx];
final DisplayContent displayContent = new DisplayContent(display, this);
addChild(displayContent, POSITION_BOTTOM);
if (displayContent.mDisplayId == DEFAULT_DISPLAY) {
mDefaultDisplay = displayContent;
}
}
calculateDefaultMinimalSizeOfResizeableTasks();
final TaskDisplayArea defaultTaskDisplayArea = getDefaultTaskDisplayArea();
defaultTaskDisplayArea.getOrCreateRootHomeTask(ON_TOP);
positionChildAt(POSITION_TOP, defaultTaskDisplayArea.mDisplayContent,
false /* includingParents */);
}
3 DisplayContent初始化构建窗口层次
DisplayContent(Display display, RootWindowContainer root) {
......
// Setup the policy and build the display area hierarchy.
mDisplayAreaPolicy = mWmService.getDisplayAreaPolicyProvider().instantiate(
mWmService, this /* content */, this /* root */, mImeWindowsContainer);
....
}
读取配置com.android.internal.R.string.config_deviceSpecificDisplayAreaPolicyProvider生产mDisplayAreaPolicyProvider 。如果为空则返回 DisplayAreaPolicy.DefaultProvider()
mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources(
mContext.getResources());
/**
* Instantiates the device-specific {@link Provider}.
*/
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);
}
}
它的Instantiate方法返回DisplayAreaPolicy。现在看看DisplayAreaPolicy.DefaultProvider()的instantiate方法。
@Override
public DisplayAreaPolicy instantiate(WindowManagerService wmService,
DisplayContent content, RootDisplayArea root,
DisplayArea.Tokens imeContainer) {
final TaskDisplayArea defaultTaskDisplayArea = new TaskDisplayArea(content, wmService,
"DefaultTaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER);
final List<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.
final HierarchyBuilder rootHierarchy = new HierarchyBuilder(root);
// Set the essential containers (even if the display doesn't support IME).
rootHierarchy.setImeContainer(imeContainer).setTaskDisplayAreas(tdaList);
if (content.isTrusted()) {
// Only trusted display can have system decorations.
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 DisplayAreaPolicyBuilder().setRootHierarchy(rootHierarchy).build(wmService);
}
创建了一个HierarchyBuilder类型的 rootHierarchy变量。它的mTaskDisplayAreas只含有一个元素,就是defaultTaskDisplayArea ,也设置rootHierarchy的mImeContainer成员。但是DisplayAreaPolicyBuilder没有设置DisplayAreaGroupBuilder。Android S中默认没有开启DisplayAreaGroup和feature。DisplayAreaContent是android S中新引入的一个类。android S中窗口层次大体如下图所示。
窗口层次结构,每一个元素的类型都是DisplayArea。具体有TaskDisplayArea,DisplayAreaGroup、DisplayArea.Tokens(非TaskDisplayArea和DisplayAreaGroup节点)和DisplayArea(feature节点)。而在application_layer左边layer的节点叫做BELOW_TASKS, 右边叫做ABOVE_TASKS。而application_layer上的节点叫做any.每一个DisplayAreaGroup单独建立一个层次hierarchy,DisplayAreaPolicyBuilder设置其RootHierarchy和众多DisplayAreaGroupHierarchy,每一个HierarchyBuilder都以其mRoot为根建立hierarchy,有几个HierarchyBuilder就有几棵树。
4 DisplayAreaPolicy 构建过程
Result build(WindowManagerService wmService) {
validate();
// Attach DA group roots to screen hierarchy before adding windows to group hierarchies.
mRootHierarchyBuilder.build(mDisplayAreaGroupHierarchyBuilders);
List<RootDisplayArea> displayAreaGroupRoots = new ArrayList<>(
mDisplayAreaGroupHierarchyBuilders.size());
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);
}
返回的DisplayAreaBuilder.Result类实现了DisplayAreaPolicy抽象类。
首先调用HierarchyBuilder类mRootHierarchyBuilder的build的方法建立DisplayContent树(根为DisplayContent)然后依次调用mDisplayAreaGroupHierarchyBuilders中每一个HierarchyBuilder类的build方法建立每一个DisplayAreaGroup树(根为DisplayAreaGroup)。
5 DisplayContent树
mRootHierarchyBuilder.build(mDisplayAreaGroupHierarchyBuilders)
/**
* Builds the {@link DisplayArea} hierarchy below root. And adds the roots of those
* {@link HierarchyBuilder} as children.
*/
private void build(@Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) {
final WindowManagerPolicy policy = mRoot.mWmService.mPolicy;
final int maxWindowLayerCount = policy.getMaxWindowLayer() + 1;
final DisplayArea.Tokens[] displayAreaForLayer =
new DisplayArea.Tokens[maxWindowLayerCount];
final Map<Feature, List<DisplayArea<WindowContainer>>> featureAreas =
new ArrayMap<>(mFeatures.size());
// mFeatures用来扩展wm的功能,目前是空的
for (int i = 0; i < mFeatures.size(); i++) {
featureAreas.put(mFeatures.get(i), new ArrayList<>());
}
// This method constructs the layer hierarchy with the following properties:
// (1) Every feature maps to a set of DisplayAreas
// (2) After adding a window, for every feature the window's type belongs to,
// it is a descendant of one of the corresponding DisplayAreas of the feature.
// (3) Z-order is maintained, i.e. if z-range(area) denotes the set of layers of windows
// within a DisplayArea:
// for every pair of DisplayArea siblings (a,b), where a is below b, it holds that
// max(z-range(a)) <= min(z-range(b))
//
// The algorithm below iteratively creates such a hierarchy:
// - Initially, all windows are attached to the root.
// - For each feature we create a set of DisplayAreas, by looping over the layers
// - if the feature does apply to the current layer, we need to find a DisplayArea
// for it to satisfy (2)
// - we can re-use the previous layer's area if:
// the current feature also applies to the previous layer, (to satisfy (3))
// and the last feature that applied to the previous layer is the same as
// the last feature that applied to the current layer (to satisfy (2))
// - otherwise we create a new DisplayArea below the last feature that applied
// to the current layer
PendingArea[] areaForLayer = new PendingArea[maxWindowLayerCount];
//**Feature feature, int minLayer, PendingArea parent**
final PendingArea root = new PendingArea(null, 0, null);
Arrays.fill(areaForLayer, root);
// Create DisplayAreas to cover all defined features.
final int size = mFeatures.size();
for (int i = 0; i < size; i++) {
// Traverse the features with the order they are defined, so that the early defined
// feature will be on the top in the hierarchy.
final Feature feature = mFeatures.get(i);
PendingArea featureArea = null;
for (int layer = 0; layer < maxWindowLayerCount; layer++) {
if (feature.mWindowLayers[layer]) {
// This feature will be applied to this window layer.
//
// We need to find a DisplayArea for it:
// We can reuse the existing one if it was created for this feature for the
// previous layer AND the last feature that applied to the previous layer is
// the same as the feature that applied to the current layer (so they are ok
// to share the same parent DisplayArea).
if (featureArea == null || featureArea.mParent != areaForLayer[layer]) {
// No suitable DisplayArea:
// Create a new one under the previous area (as parent) for this layer.
featureArea = new PendingArea(feature, layer, areaForLayer[layer]);
areaForLayer[layer].mChildren.add(featureArea);
}
areaForLayer[layer] = featureArea;
} else {
// This feature won't be applied to this window layer. If it needs to be
// applied to the next layer, we will need to create a new DisplayArea for
// that.
featureArea = null;
}
}
}
// Create Tokens as leaf for every layer.
PendingArea leafArea = null;
int leafType = LEAF_TYPE_TOKENS;
for (int layer = 0; layer < maxWindowLayerCount; layer++) {
int type = typeOfLayer(policy, layer);
// Check whether we can reuse the same Tokens with the previous layer. This happens
// if the previous layer is the same type as the current layer AND there is no
// feature that applies to only one of them.
/* 存在leafArea,则如果leafArea.mParet就是指向areaForLayer[layer],
且leaftype等于该layer的tye,则复用该leafArea,否则则重新创建leafArea*/
if (leafArea == null || //上一层有节点
leafArea.mParent != areaForLayer[layer] //同属于同一个pendingArea
|| type != leafType //窗口类型相同。
) {
// Create a new Tokens for this layer.
leafArea = new PendingArea(null /* feature */, layer, areaForLayer[layer]);
areaForLayer[layer].mChildren.add(leafArea);
leafType = type;
//为layer 2 应用layer
if (leafType == LEAF_TYPE_TASK_CONTAINERS) {
// We use the passed in TaskDisplayAreas for task container type of layer.
// Skip creating Tokens even if there is no TDA.
addTaskDisplayAreasToApplicationLayer(areaForLayer[layer]);
addDisplayAreaGroupsToApplicationLayer(areaForLayer[layer],
displayAreaGroupHierarchyBuilders);
leafArea.mSkipTokens = true;
//为layer 15 和16 ,输入法layer
} else if (leafType == LEAF_TYPE_IME_CONTAINERS) {
// We use the passed in ImeContainer for ime container type of layer.
// Skip creating Tokens even if there is no ime container.
leafArea.mExisting = mImeContainer;
leafArea.mSkipTokens = true;
}
}
leafArea.mMaxLayer = layer;
}
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.
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.
mRoot.onHierarchyBuilt(mFeatures, displayAreaForLayer, featureAreas);
}
PendingArea(Feature feature, int minLayer, PendingArea parent) {
mMinLayer = minLayer;
mFeature = feature;
mParent = parent;
成员
static class PendingArea {
final int mMinLayer;
final ArrayList<PendingArea> mChildren = new ArrayList<>();
final Feature mFeature;
final PendingArea mParent;
int mMaxLayer;
/** If not {@code null}, use this instead of creating a {@link DisplayArea.Tokens}. */
@Nullable DisplayArea mExisting;
/**
* Whether to skip creating a {@link DisplayArea.Tokens} if {@link #mExisting} is
* {@code null}.
*
* <p>This will be set for {@link HierarchyBuilder#LEAF_TYPE_IME_CONTAINERS} and
* {@link HierarchyBuilder#LEAF_TYPE_TASK_CONTAINERS}, because we don't want to create
* {@link DisplayArea.Tokens} for them even if they are not set.
*/
boolean mSkipTokens = false;
}
PendingArea的mSkipTokens 表示这不是一个tokens节点,后面非feature树的时候会说,而mFeature不为null,则表示这是一个feature节点。
HierarchyBuilder的builder方法先构建PendingArea树,最后在将PedingArea树转为DisplayArea树
构建PendingArea树可以分为两个部分,第一个部分根据mFeatures所应用的layer建立feature节点,第二个部分在第一部分构建的PendingArea树基础上再每一个layer都建立tokens节点,构建tokens树,其中如果是应用layer(application_layer,编号为2),则HierarchyBuilder的mTaskDisplayArea和传进来的mDisplayAreaGroupHierarchyBuilders建立PendingArea节点。
5.1 features 树建立
feature实质是对相同性质的layer的分组,结果保存在areaForLayer数组中。数组中的每个元素因其mParent成员而形成树。areForLayer存储的是叶子节点。
对于每一个feature,在该feature支持的每一layer中创建一个PedingArea节点,为了效率,如果前一个相邻layer的PedingArea节点已经创建,且该节点的父亲为areaForLayer[layer],则就不必创建新的PedingArea了,就用前一个PedingArea。
在 Android 12 上,Feature 正式派上用场了,原生添加了以下 Feature:
- WindowedMagnification: 屏幕放大功能,通過 SystemUI mirrorSurface 该节点实现内容拷贝,详见 WindowMagnificationGestureHandler#toggleMagnification
- HideDisplayCutout: 隐藏刘海屏功能,开启后,该节点将不会延伸到刘海屏区域(除了状态栏导航栏等窗口,因为不在节点控制范围之内)
- OneHandedBackgroundPanel: 单手模式下此节点会挂载一个纯色图层(在壁纸图层之下),防止深色模式下分辨不出单手模式
- OneHanded: 单手模式下相关节点都会做一个向下的位移
FullscreenMagnification: 屏幕放大功能,通过无障碍服 - FullScreenMagnificationController.SpecAnimationBridge#setMagnificationSpecLocked 最后调用 DisplayContent#applyMagnificationSpec 方法实现节点放大。不过源码中并不是通过这个 Feature 来实现相关层级放大的,改造得还不彻底
- ImePlaceholder: 特殊情况下用来放置输入法的节点
建立过程提供两种理解:
1 图形理解
我们知道,Android 系统是有 Z 轴概念的,不同的窗口有不同的高度,所有的窗口类型对应到 WMS 都会有一个 layer 值,layer 越大,显示在越上面,WMS 规定 1~36 层级,每一个 Feature 都指定了它所能影响到的 layer 层。这里用颜色对不同 Feature 能影响 layer 图层进行颜色标记:
上移之后,我们得到了最终的图表,接下来用以下规则进行层级构建:
同一行相邻的同色块变成一个 Feature 节点,从左到右根据颜色不断生成节点,同一行所有节点挂在同一个父节点下,父节点就是垂直正上方一行的色块对应的节点,为最末端所有 Feature 节点再添加一个节点,根据子节点代表的 layer 不一样,最后添加的节点也不一样除了 layer 是 2、15 和 16 外,挂载 DisplayArea.Tokens(这类节点后续只能添加 WindowToken 节点)layer = 2 (也就是 APPLICATION_LAYER)的节点,挂载 TaskDisplayArealayer ,等于 15 和 16 的节点,挂载 ImeContainer
2 本质理解
第一种理解过于表象,没有将DisplayArea的概念体现出来,本质上feature树的建立过程如下:屏幕的所有layer属于屏幕这个pendingArea所有,假设feature 1作用于1、5 layer,则在屏幕pendingArea的中获得1、5 layer用于建立自己的pendingArea用于显示(窗口显示在上面),则就建立2个pedingArea,且它们的父亲为屏幕pendingArea。feature 2 作用于1 、2、3layer,则其需要从feature 1的pendingArea 1中获取layer1 ,从屏幕pendingArea中获取layer 2 、3。对于layer 1,产生feature2 自己的pendingArea ,其父亲为feature 1 的pendingArea,而对于layer 2、 3由于其相邻且都是从一个pendingArea中分配的,因此只建立一个pedingArea,其mMinLayer为2,mManxLayer 为3,父亲为屏幕pendingLayer。
通过上述构建规则后,我们可以获得一个树形的层级,并且这棵树有以下特点:
树的最末端节点对应一个 layer 范围,同一个 layer 值只有一个末端节点与之对应为所有 Feature 都生成了对应的父节点,用以控制其所能影响的 layer。生成了这棵树后,我们会保存两样东西:
- areaForLayer数组,所有 layer 值对应的最末端节点,方便我们后续根据窗口类型添加节点
- 以 Map<Feature, List> 形式保存的所有 Feature 节点,方便我们后取出某 Feature 对应的所有节点
5.2tokens树和TaskDisplayArea、DisplayAreaGroup节点
和5.1构建的feature树类似,tokens节点作用于所有layer。构建方式和5.1一样,唯一的区别是复用的条件除了和上一layer的pendingArea同属于一个pendingArea外,还需要两个layer的type相同。typeOfLayer指该layer上的叶子节点类型。对于应用层,即APPLICATION_LAYER(layer值为2),类型为task containers,对于layer为等于 15 和 16 的节点,类型为IME_CONTAINERS,其它layer,则类型为tokens。其实ime containers也是一种tokens。
private static int typeOfLayer(WindowManagerPolicy policy, int layer) {
if (layer == APPLICATION_LAYER) {
return LEAF_TYPE_TASK_CONTAINERS;
} else if (layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD)
|| layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD_DIALOG)) {
return LEAF_TYPE_IME_CONTAINERS;
} else {
return LEAF_TYPE_TOKENS;
}
}
如上图所示的构建过程,layer 1、0同属于feature 2的第一个pedingAarea,且layer 1与2 类型相同,因此合成一个PendingArea,layer 2 、3、4虽然同属于同一个pendingArea,但是layer 2与其它layer 类型不同,因此layer 2单独生成一个pendingArea,3和4生成同一个pendingArea。layer 15 和layer 16虽然类型都是ime container,但是它们分属不同的pendingArea,因此不能合成一个pendingArea,所以生成pendingArea 15和pendingArea16,这两个是特殊的tokens,它们的mSkipTokens为false,mExisting为mImeContainer(mImeContainer是传进来的值)表示后续DisplayArea树的构建过程中,不自动为其生产Tokens节点,而是用mExisting替代(mExisting就是DisplayArea类型)。tokens节点生成之后保存在areaForLayer数组中。
生成了tokens pendingArea树之后,对于layer 2,还要根据mTaskDisplayAreas的值,为其生成pendingArea,其mSkipTokens为false,mExisting为TaskDisplayArea对象。同时如果传进来DisplayAreaGroup集合,还要为其生成对应定pendingArea,其mSkipTokens为false,mExisting为DisplayAreaGroup对象。TaskDIsplayArea和DisplayAreaGroup节点不记录在areaForLayer数组中。
/** Adds all {@link TaskDisplayArea} to the application layer. */
private void addTaskDisplayAreasToApplicationLayer(PendingArea parentPendingArea) {
final int count = mTaskDisplayAreas.size();
for (int i = 0; i < count; i++) {
PendingArea leafArea =
new PendingArea(null /* feature */, APPLICATION_LAYER, parentPendingArea);
leafArea.mExisting = mTaskDisplayAreas.get(i);
leafArea.mMaxLayer = APPLICATION_LAYER;
parentPendingArea.mChildren.add(leafArea);
}
}
/** Adds roots of the DisplayAreaGroups to the application layer. */
private void addDisplayAreaGroupsToApplicationLayer(
DisplayAreaPolicyBuilder.PendingArea parentPendingArea,
@Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) {
if (displayAreaGroupHierarchyBuilders == null) {
return;
}
final int count = displayAreaGroupHierarchyBuilders.size();
for (int i = 0; i < count; i++) {
DisplayAreaPolicyBuilder.PendingArea
leafArea = new DisplayAreaPolicyBuilder.PendingArea(
null /* feature */, APPLICATION_LAYER, parentPendingArea);
leafArea.mExisting = displayAreaGroupHierarchyBuilders.get(i).mRoot;
leafArea.mMaxLayer = APPLICATION_LAYER;
parentPendingArea.mChildren.add(leafArea);
}
}
pendingArea树中的节点类型怎么区分呢?如果pendingArea的PendingArea的mFeature不为null,则表示这是一个feature节点。pendingArea的mSkipTokens 为false,表示 后面转化为DisplayArea树的时候不要为其创建Tokens类型节点,而pendingArea的mExisting 表示 创建该类型的节点。
综上,对于
- feature节点:pendingArea 的mFeature不为null
- task container节点:mSkipTokens 为false,mExisting =TaskDisplayArea或者DisplayAreaGroup
- ime container节点:mSkipTokens 为false,mExisting =mImeContainer。
5.3 构建DisplayArea树
5.1 和5.2 建立的是PendingArea树,我们需要将其转化为DisplayArea树。通过调用该pendingArea树的根节点的instantiateChildren方法,建立DisplayArea树,结果保存在mRoot成员中。
调用 root.instantiateChildren(mRoot, displayAreaForLayer, 0, featureAreas);
建立以mRoot为根的DisplayArea树。输出为mRoot、displayAreaForLayer和featureAreas。displayAreaForLayer为Tokens数组,就像上面pendingArea数组一样,displayAreaForLayer存储生成的DisplayArea树的叶子节点,而featureAreas则是feature到DisplayArea数组的map。
void instantiateChildren(DisplayArea<DisplayArea> parent, DisplayArea.Tokens[] areaForLayer,
int level, Map<Feature, List<DisplayArea<WindowContainer>>> areas) {
mChildren.sort(Comparator.comparingInt(pendingArea -> pendingArea.mMinLayer));
for (int i = 0; i < mChildren.size(); i++) {
final PendingArea child = mChildren.get(i);
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;
}
parent.addChild(area, WindowContainer.POSITION_TOP);
if (child.mFeature != null) {
areas.get(child.mFeature).add(area);
}
child.instantiateChildren(area, areaForLayer, level + 1, areas);
}
}
@Nullable
private DisplayArea createArea(DisplayArea<DisplayArea> parent,
DisplayArea.Tokens[] areaForLayer) {
if (mExisting != null) {
if (mExisting.asTokens() != null) {
// Store the WindowToken container for layers
fillAreaForLayers(mExisting.asTokens(), areaForLayer);
}
return mExisting;
}
if (mSkipTokens) {
return null;
}
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;
}
if (mFeature == null) {
final DisplayArea.Tokens leaf = new DisplayArea.Tokens(parent.mWmService, type,
"Leaf:" + mMinLayer + ":" + mMaxLayer);
fillAreaForLayers(leaf, areaForLayer);
return leaf;
} else {
return mFeature.mNewDisplayAreaSupplier.create(parent.mWmService, type,
mFeature.mName + ":" + mMinLayer + ":" + mMaxLayer, mFeature.mId);
}
}
在layer 2之上的DisplayArea的type值为ABOVE_TASKS,之下为BELOW_TASKS,相等则type为ANY,表示任何一个TaskDIsplayArea或者DisplayAareaGroup的意思
6 DisplayAreaGroup树
构建方式和DisplayContent树是一样的,都是调用HierarchyBuilder的Builder方法,唯一区别是DisplayAreaGroup树没有传入参数。
Result build(WindowManagerService wmService) {
validate();
// Attach DA group roots to screen hierarchy before adding windows to group hierarchies.
mRootHierarchyBuilder.build(mDisplayAreaGroupHierarchyBuilders);
List<RootDisplayArea> displayAreaGroupRoots = new ArrayList<>(
mDisplayAreaGroupHierarchyBuilders.size());
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);
}
窗口层次结构,每一个元素的类型都是DisplayArea。具体有TaskDisplayArea,DisplayAreaGroup、DisplayArea.Tokens(非TaskDisplayArea和DisplayAreaGroup节点)和DisplayArea(feature节点)。而在application_layer左边layer的节点叫做BELOW_TASKS, 右边叫做ABOVE_TASKS。而application_layer上的节点叫做any.每一个DisplayAreaGroup单独建立一个DisplayAreaGroup树,DisplayAreaPolicyBuilder设置其RootHierarchy(DisplayContent树)和众多DisplayAreaGroupHierarchy(DisplayAreaGroup树),每一个HierarchyBuilder都以其mRoot为根建立hierarchy,有几个HierarchyBuilder就有几棵树。
mSelectRootForWindowFunc是为了在window创建的时候却定该window应该在哪一颗树下。一般采用默认实现DefaultSelectRootForWindowFunction 。传入windowType和featureID,在RootDisplayArea中找mFeatureId和它相同的RootDisplayArea。
/**
* When a window is created, the policy will use this function, which takes window type and
* options, to select the {@link RootDisplayArea} to place that window in. The selected root
* can be either the one of the {@link #mRootHierarchyBuilder} or the one of any of the
* {@link #mDisplayAreaGroupHierarchyBuilders}.
* @see DefaultSelectRootForWindowFunction as an example.
**/
@Nullable private BiFunction<Integer, Bundle, RootDisplayArea> mSelectRootForWindowFunc;
第一个参数为windowtype,第二个参数为包含有featureID,最后一个为返回的RootDisplayArea。
/**
* The default function to be used for finding {@link RootDisplayArea} for window to be attached
* to if there is no other function set through {@link #setSelectRootForWindowFunc(BiFunction)}.
*
* When a window is created with {@link Bundle} options, this function will select the
* {@link RootDisplayArea} based on the options. Returns {@link #mDisplayRoot} if there is no
* match found.
*/
private static class DefaultSelectRootForWindowFunction implements
BiFunction<Integer, Bundle, RootDisplayArea> {
final RootDisplayArea mDisplayRoot;
final List<RootDisplayArea> mDisplayAreaGroupRoots;
DefaultSelectRootForWindowFunction(RootDisplayArea displayRoot,
List<RootDisplayArea> displayAreaGroupRoots) {
mDisplayRoot = displayRoot;
mDisplayAreaGroupRoots = Collections.unmodifiableList(displayAreaGroupRoots);
}
@Override
public RootDisplayArea apply(Integer windowType, Bundle options) {
if (mDisplayAreaGroupRoots.isEmpty()) {
return mDisplayRoot;
}
// Select the RootDisplayArea set in options.
if (options != null && options.containsKey(KEY_ROOT_DISPLAY_AREA_ID)) {
final int rootId = options.getInt(KEY_ROOT_DISPLAY_AREA_ID);
if (mDisplayRoot.mFeatureId == rootId) {
return mDisplayRoot;
}
for (int i = mDisplayAreaGroupRoots.size() - 1; i >= 0; i--) {
if (mDisplayAreaGroupRoots.get(i).mFeatureId == rootId) {
return mDisplayAreaGroupRoots.get(i);
}
}
}
return mDisplayRoot;
}
}
7 DisplayAreaPolicy
最后返回DisplayAreaPolicy,这是一个抽象类,实现类为DisplayAreaPolicyBuilder.Result.
static class Result extends DisplayAreaPolicy {
final List<RootDisplayArea> mDisplayAreaGroupRoots;
final BiFunction<Integer, Bundle, RootDisplayArea> mSelectRootForWindowFunc;
private final TaskDisplayArea mDefaultTaskDisplayArea;
Result(WindowManagerService wmService, RootDisplayArea root,
List<RootDisplayArea> displayAreaGroupRoots,
BiFunction<Integer, Bundle, RootDisplayArea> selectRootForWindowFunc) {
super(wmService, root);
mDisplayAreaGroupRoots = Collections.unmodifiableList(displayAreaGroupRoots);
mSelectRootForWindowFunc = selectRootForWindowFunc;
// Cache the default TaskDisplayArea for quick access.
mDefaultTaskDisplayArea = mRoot.getItemFromTaskDisplayAreas(taskDisplayArea ->
taskDisplayArea.mFeatureId == FEATURE_DEFAULT_TASK_CONTAINER
? taskDisplayArea
: null);
if (mDefaultTaskDisplayArea == null) {
throw new IllegalStateException(
"No display area with FEATURE_DEFAULT_TASK_CONTAINER");
}
}
**
* Policy that manages {@link DisplayArea}.
*/
public abstract class DisplayAreaPolicy {
protected final WindowManagerService mWmService;
/**
* The {@link RootDisplayArea} of the whole logical display. All {@link DisplayArea}s must be
* (direct or indirect) descendants of this area.
*/
protected final RootDisplayArea mRoot;
/**
* Constructs a new {@link DisplayAreaPolicy}
*
* @param wmService the window manager service instance
* @param root the root display area under which the policy operates
*/
protected DisplayAreaPolicy(WindowManagerService wmService, RootDisplayArea root) {
mWmService = wmService;
mRoot = root;
}
核心成员为mRoot(DisplayContent树)、mDisplayAreaGroupRoots(DisplayAreaGroup树)、mSelectRootForWindowFunc、mDefaultTaskDisplayArea。
mDefaultTaskDisplayArea为了快速访问而存在,
// Cache the default TaskDisplayArea for quick access.
mDefaultTaskDisplayArea = mRoot.getItemFromTaskDisplayAreas(taskDisplayArea ->
taskDisplayArea.mFeatureId == FEATURE_DEFAULT_TASK_CONTAINER
? taskDisplayArea
: null);
8 Root Home Task
ATMS的mRootWindowContainer调用setWindowManager创建DisplayContent后,再调用getDefaultTaskDisplayArea获取默认的TaskDisplayArea,然后调用其getOrCreateRootHomeTask方法。
从DisplayManagerService中获取屏幕集合,然后为每个屏幕创建DisplayContent
```cpp
void setWindowManager(WindowManagerService wm) {
mWindowManager = wm;
mDisplayManager = mService.mContext.getSystemService(DisplayManager.class);
mDisplayManager.registerDisplayListener(this, mService.mUiHandler);
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
final Display[] displays = mDisplayManager.getDisplays();
for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {
final Display display = displays[displayNdx];
final DisplayContent displayContent = new DisplayContent(display, this);
addChild(displayContent, POSITION_BOTTOM);
//这里----------设置了mDefaultDisplay
if (displayContent.mDisplayId == DEFAULT_DISPLAY) {
mDefaultDisplay = displayContent;
}
}
calculateDefaultMinimalSizeOfResizeableTasks();
final TaskDisplayArea defaultTaskDisplayArea = getDefaultTaskDisplayArea();
//这里------------------------------------------------
defaultTaskDisplayArea.getOrCreateRootHomeTask(ON_TOP);
positionChildAt(POSITION_TOP, defaultTaskDisplayArea.mDisplayContent,
false /* includingParents */);
}
```cpp
/* Get the default display area on the device dedicated to app windows. This one should be used
* only as a fallback location for activity launches when no target display area is specified,
* or for cases when multi-instance is not supported yet (like Split-screen, Freeform, PiP or
* Recents).
*/
TaskDisplayArea getDefaultTaskDisplayArea() {
return mDefaultDisplay.getDefaultTaskDisplayArea();
}
mDefaultDisplay是DisplayContent类型。
TaskDisplayArea getDefaultTaskDisplayArea() {
return mDisplayAreaPolicy.getDefaultTaskDisplayArea();
}
@Override
public TaskDisplayArea getDefaultTaskDisplayArea() {
return mDefaultTaskDisplayArea;
}
其本质就是返回DisplayAreaPolicyBuilder.Result的mDefaultTaskDisplayArea成员,该成员的设置第7节以及说过。
现在看看
defaultTaskDisplayArea.getOrCreateRootHomeTask(ON_TOP);
@Nullable
Task getOrCreateRootHomeTask() {
return getOrCreateRootHomeTask(false /* onTop */);
}
/**
* Returns the existing root home task or creates and returns a new one if it should exist
* for the display.
*
* @param onTop Only be used when there is no existing root home task. If true the root home
* task will be created at the top of the display, else at the bottom.
*/
@Nullable
Task getOrCreateRootHomeTask(boolean onTop) {
Task homeTask = getRootHomeTask();
// Take into account if this TaskDisplayArea can have a home task before trying to
// create the root task
if (homeTask == null && canHostHomeTask()) {
homeTask = createRootTask(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, onTop);
}
return homeTask;
}
@Nullable
Task getRootHomeTask() {
return mRootHomeTask;
}
/**
* True if this TaskDisplayArea can have a home task
* {@link WindowConfiguration#ACTIVITY_TYPE_HOME}
*/
private final boolean mCanHostHomeTask;
TaskDisplayArea(DisplayContent displayContent, WindowManagerService service, String name,
int displayAreaFeature, boolean createdByOrganizer) {
this(displayContent, service, name, displayAreaFeature, createdByOrganizer,
true /* canHostHomeTask */);
}
TaskDisplayArea(DisplayContent displayContent, WindowManagerService service, String name,
int displayAreaFeature, boolean createdByOrganizer,
boolean canHostHomeTask) {
super(service, Type.ANY, name, displayAreaFeature);
mDisplayContent = displayContent;
mRootWindowContainer = service.mRoot;
mAtmService = service.mAtmService;
mCreatedByOrganizer = createdByOrganizer;
mCanHostHomeTask = canHostHomeTask; //为true
}
创建mRootHomeTask
Task createRootTask(int windowingMode, int activityType, boolean onTop) {
return createRootTask(windowingMode, activityType, onTop, null /* activityOptions */);
}
/**
* A convinenit method of creating a root task by providing windowing mode and activity type
* on this display.
*
* @param windowingMode The windowing mode the root task should be created in. If
* {@link WindowConfiguration#WINDOWING_MODE_UNDEFINED} then the
* root task will inherit its parent's windowing mode.
* @param activityType The activityType the root task should be created in. If
* {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} then the
* root task will be created in
* {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}.
* @param onTop If true the root task will be created at the top of the display,
* else at the bottom.
* @param opts The activity options.
* @return The newly created root task.
*/
Task createRootTask(int windowingMode, int activityType, boolean onTop, ActivityOptions opts) {
return new Task.Builder(mAtmService)
.setWindowingMode(windowingMode)
.setActivityType(activityType)
.setParent(this)
.setOnTop(onTop)
.setActivityOptions(opts)
.build();
}
Task build() {
if (mParent != null && mParent instanceof TaskDisplayArea) {
validateRootTask((TaskDisplayArea) mParent);
}
if (mActivityInfo == null) {
mActivityInfo = new ActivityInfo();
mActivityInfo.applicationInfo = new ApplicationInfo();
}
mUserId = UserHandle.getUserId(mActivityInfo.applicationInfo.uid);
mTaskAffiliation = mTaskId;
mLastTimeMoved = System.currentTimeMillis();
mNeverRelinquishIdentity = true;
mCallingUid = mActivityInfo.applicationInfo.uid;
mCallingPackage = mActivityInfo.packageName;
mResizeMode = mActivityInfo.resizeMode;
mSupportsPictureInPicture = mActivityInfo.supportsPictureInPicture();
if (mActivityOptions != null) {
mRemoveWithTaskOrganizer = mActivityOptions.getRemoveWithTaskOranizer();
}
final Task task = buildInner();
task.mHasBeenVisible = mHasBeenVisible;
// Set activity type before adding the root task to TaskDisplayArea, so home task can
// be cached, see TaskDisplayArea#addRootTaskReferenceIfNeeded().
if (mActivityType != ACTIVITY_TYPE_UNDEFINED) {
task.setActivityType(mActivityType);
}
if (mParent != null) {
if (mParent instanceof Task) {
final Task parentTask = (Task) mParent;
parentTask.addChild(task, mOnTop ? POSITION_TOP : POSITION_BOTTOM,
(mActivityInfo.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
} else {
mParent.addChild(task, mOnTop ? POSITION_TOP : POSITION_BOTTOM);
}
}
// Set windowing mode after attached to display area or it abort silently.
if (mWindowingMode != WINDOWING_MODE_UNDEFINED) {
task.setWindowingMode(mWindowingMode, true /* creating */);
}
return task;
}
将创建的Root Home Task加入TaskDiplayArea中。
9 将mDefaultDisplay作为最右孩子添加到RootWindowContainer.mChildren中
void setWindowManager(WindowManagerService wm) {
mWindowManager = wm;
mDisplayManager = mService.mContext.getSystemService(DisplayManager.class);
mDisplayManager.registerDisplayListener(this, mService.mUiHandler);
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
final Display[] displays = mDisplayManager.getDisplays();
for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {
final Display display = displays[displayNdx];
final DisplayContent displayContent = new DisplayContent(display, this);
addChild(displayContent, POSITION_BOTTOM);
if (displayContent.mDisplayId == DEFAULT_DISPLAY) {
mDefaultDisplay = displayContent;
}
}
calculateDefaultMinimalSizeOfResizeableTasks();
final TaskDisplayArea defaultTaskDisplayArea = getDefaultTaskDisplayArea();
defaultTaskDisplayArea.getOrCreateRootHomeTask(ON_TOP);
//这里---------------------------------
positionChildAt(POSITION_TOP, defaultTaskDisplayArea.mDisplayContent,
false /* includingParents */);
}
调用父类WindowContainer的方法
```cpp
/**
* Move a child from it's current place in siblings list to the specified position,
* with an option to move all its parents to top.
* @param position Target position to move the child to.
* @param child Child to move to selected position.
* @param includingParents Flag indicating whether we need to move the entire branch of the
* hierarchy when we're moving a child to {@link #POSITION_TOP} or
* {@link #POSITION_BOTTOM}. When moving to other intermediate positions
* this flag will do nothing.
*/
@CallSuper
void positionChildAt(int position, E child, boolean includingParents) {
if (child.getParent() != this) {
throw new IllegalArgumentException("positionChildAt: container=" + child.getName()
+ " is not a child of container=" + getName()
+ " current parent=" + child.getParent());
}
if (position >= mChildren.size() - 1) {
position = POSITION_TOP;
} else if (position <= 0) {
position = POSITION_BOTTOM;
}
switch (position) {
case POSITION_TOP:
if (mChildren.peekLast() != child) {
mChildren.remove(child);
mChildren.add(child);
onChildPositionChanged(child);
}
if (includingParents && getParent() != null) {
getParent().positionChildAt(POSITION_TOP, this /* child */,
true /* includingParents */);
}
break;
case POSITION_BOTTOM:
if (mChildren.peekFirst() != child) {
mChildren.remove(child);
mChildren.addFirst(child);
onChildPositionChanged(child);
}
if (includingParents && getParent() != null) {
getParent().positionChildAt(POSITION_BOTTOM, this /* child */,
true /* includingParents */);
}
break;
default:
// TODO: Removing the child before reinserting requires the caller to provide a
// position that takes into account the removed child (if the index of the
// child < position, then the position should be adjusted). We should consider
// doing this adjustment here and remove any adjustments in the callers.
if (mChildren.indexOf(child) != position) {
mChildren.remove(child);
mChildren.add(position, child);
onChildPositionChanged(child);
}
}
}
10 各类窗口添加到DisplayContent树
10**.1 非Activity的窗口作为DisplayArea.Tokens的孩子添加到wm层次结构**
如:WallpaperWindowToken加入到Leaf0:1,AssistPreviewPanel DockedStackDivider加入到Leaf3:14,RoundCorner NavigationBar StatusBar等加入到Leaf17:34。它们都是非Activity的窗口,这些窗口在创建时,进行添加:
protected WindowToken(WindowManagerService service, IBinder _token, int type,
boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens,
boolean roundedCornerOverlay, boolean fromClientToken, @Nullable Bundle options) {
super(service);
token = _token;
windowType = type;
mOptions = options;
mPersistOnEmpty = persistOnEmpty;
mOwnerCanManageAppTokens = ownerCanManageAppTokens;
mRoundedCornerOverlay = roundedCornerOverlay;
mFromClientToken = fromClientToken;
if (dc != null) {
//这里---------------------
dc.addWindowToken(token, this);
}
}
void addWindowToken(IBinder binder, WindowToken token) {
final DisplayContent dc = mWmService.mRoot.getWindowTokenDisplay(token);
if (dc != null) {
// We currently don't support adding a window token to the display if the display
// already has the binder mapped to another token. If there is a use case for supporting
// this moving forward we will either need to merge the WindowTokens some how or have
// the binder map to a list of window tokens.
throw new IllegalArgumentException("Can't map token=" + token + " to display="
+ getName() + " already mapped to display=" + dc + " tokens=" + dc.mTokenMap);
}
if (binder == null) {
throw new IllegalArgumentException("Can't map token=" + token + " to display="
+ getName() + " binder is null");
}
if (token == null) {
throw new IllegalArgumentException("Can't map null token to display="
+ getName() + " binder=" + binder);
}
mTokenMap.put(binder, token);
//非Activity窗口加入到DisplayContent树种
if (token.asActivityRecord() == null) {
// Set displayContent for non-app token to prevent same token will add twice after
// onDisplayChanged.
// TODO: Check if it's fine that super.onDisplayChanged of WindowToken
// (WindowsContainer#onDisplayChanged) may skipped when token.mDisplayContent assigned.
token.mDisplayContent = this;
// Add non-app token to container hierarchy on the display. App tokens are added through
// the parent container managing them (e.g. Tasks).
//findAreaForToken找到该WindowToken应该要挂的DisplayArea
final DisplayArea.Tokens da = findAreaForToken(token).asTokens();
da.addChild(token);
}
}
/**
* Finds the {@link DisplayArea} for the {@link WindowToken} to attach to.
* <p>
* Note that the differences between this API and
* {@link RootDisplayArea#findAreaForTokenInLayer(WindowToken)} is that this API finds a
* {@link DisplayArea} in {@link DisplayContent} level, which may find a {@link DisplayArea}
* from multiple {@link RootDisplayArea RootDisplayAreas} under this {@link DisplayContent}'s
* hierarchy, while {@link RootDisplayArea#findAreaForTokenInLayer(WindowToken)} finds a
* {@link DisplayArea.Tokens} from a {@link DisplayArea.Tokens} list mapped to window layers.
* </p>
*
* @see DisplayContent#findAreaForTokenInLayer(WindowToken)
*/
DisplayArea findAreaForToken(WindowToken windowToken) {
return findAreaForWindowType(windowToken.getWindowType(), windowToken.mOptions,
windowToken.mOwnerCanManageAppTokens, windowToken.mRoundedCornerOverlay);
}
DisplayArea findAreaForWindowType(int windowType, Bundle options,
boolean ownerCanManageAppToken, boolean roundedCornerOverlay) {
// TODO(b/159767464): figure out how to find an appropriate TDA.
if (windowType >= FIRST_APPLICATION_WINDOW && windowType <= LAST_APPLICATION_WINDOW) {
return getDefaultTaskDisplayArea();
}
// Return IME container here because it could be in one of sub RootDisplayAreas depending on
// the focused edit text. Also, the RootDisplayArea choosing strategy is implemented by
// the server side, but not mSelectRootForWindowFunc customized by OEM.
if (windowType == TYPE_INPUT_METHOD || windowType == TYPE_INPUT_METHOD_DIALOG) {
return getImeContainer();
}
return mDisplayAreaPolicy.findAreaForWindowType(windowType, options,
ownerCanManageAppToken, roundedCornerOverlay);
}
输入法窗口添加到mImeWindowsContainers,其他则调用mDisplayAreaPolicy
@Override
public DisplayArea.Tokens findAreaForWindowType(int type, Bundle options,
boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) {
return mSelectRootForWindowFunc.apply(type, options).findAreaForWindowTypeInLayer(type,
ownerCanManageAppTokens, roundedCornerOverlay);
}
mSelectRootForWindowFunc用于寻找windowToken应该挂载的DisplayArea树的root。它的产生过程如下:
Result build(WindowManagerService wmService) {
validate();
// Attach DA group roots to screen hierarchy before adding windows to group hierarchies.
mRootHierarchyBuilder.build(mDisplayAreaGroupHierarchyBuilders);
List<RootDisplayArea> displayAreaGroupRoots = new ArrayList<>(
mDisplayAreaGroupHierarchyBuilders.size());
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);
}
/* The default function to be used for finding {@link RootDisplayArea} for window to be attached
* to if there is no other function set through {@link #setSelectRootForWindowFunc(BiFunction)}.
*
* When a window is created with {@link Bundle} options, this function will select the
* {@link RootDisplayArea} based on the options. Returns {@link #mDisplayRoot} if there is no
* match found.
*/
private static class DefaultSelectRootForWindowFunction implements
BiFunction<Integer, Bundle, RootDisplayArea> {
final RootDisplayArea mDisplayRoot;
final List<RootDisplayArea> mDisplayAreaGroupRoots;
DefaultSelectRootForWindowFunction(RootDisplayArea displayRoot,
List<RootDisplayArea> displayAreaGroupRoots) {
mDisplayRoot = displayRoot;
mDisplayAreaGroupRoots = Collections.unmodifiableList(displayAreaGroupRoots);
}
@Override
public RootDisplayArea apply(Integer windowType, Bundle options) {
if (mDisplayAreaGroupRoots.isEmpty()) {
return mDisplayRoot;
}
// Select the RootDisplayArea set in options.
if (options != null && options.containsKey(KEY_ROOT_DISPLAY_AREA_ID)) {
final int rootId = options.getInt(KEY_ROOT_DISPLAY_AREA_ID);
if (mDisplayRoot.mFeatureId == rootId) {
return mDisplayRoot;
}
for (int i = mDisplayAreaGroupRoots.size() - 1; i >= 0; i--) {
if (mDisplayAreaGroupRoots.get(i).mFeatureId == rootId) {
return mDisplayAreaGroupRoots.get(i);
}
}
}
return mDisplayRoot;
}
}
如果option设置了KEY_ROOT_DISPLAY_AREA_ID则根据这个获取root,否则返回mDisplayRoot,即DisplayContent
10.2 Activity的窗口作为defaultTaskDisplayArea的孩子添加到wm层次结构
ActivityRecord extends WindowToken