如何通过dump中的内容找到对应的代码?
我们dump窗口层级发现会有很多信息,adb shell dumpsys activity containers
这里我们以其中的DefaultTaskDisplayArea为例
在源码的framework目录下查找该字符串,找到对应的代码就可以通过打印堆栈或者搜索代码跟踪的方式找到其调用逻辑
final TaskDisplayArea defaultTaskDisplayArea =
new TaskDisplayArea(content, wmService,
"DefaultTaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER);
也就是这一句
当然我们上篇文章也讲到了DisplayContent代表的屏幕的DisplayArea层级结构的根节点,我们可以直接从DisplayContent.java的构造方法出发,追踪其流程
DisplayContent初始化
代码路径:/frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
/**
* Create new {@link DisplayContent} instance, add itself to the root window container and
* initialize direct children.
* @param display May not be null.
* @param root {@link RootWindowContainer}
*/
DisplayContent(Display display, RootWindowContainer root) {
super(root.mWindowManager, "DisplayContent", FEATURE_ROOT);
......
final Transaction pendingTransaction = getPendingTransaction();
configureSurfaces(pendingTransaction);
pendingTransaction.apply();
......
}
创建新的DisplayContent实例,将其自身添加到根窗口容器并初始化直接子级
FEATURE_ROOT,对应DisplayContent,一个屏幕上的根DisplayArea,也就是dump中的Display节点。
这里我们主要关注一下configureSurfaces(pendingTransaction);
/**
* Configures the surfaces hierarchy for DisplayContent
* This method always recreates the main surface control but reparents the children
* if they are already created.
* @param transaction as part of which to perform the configuration
*/
private void configureSurfaces(Transaction transaction) {
final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession)
.setOpaque(true)
.setContainerLayer()
.setCallsite("DisplayContent");
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
mDisplayAreaPolicy = mWmService.getDisplayAreaPolicyProvider().instantiate(
mWmService, this /* content */, this /* root */,
mImeWindowsContainer);
}
......
}
通过DisplayContent来配置图层结构
DisplayAreaPolicy初始化
代码路径:/frameworks/base/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
mDisplayAreaPolicy = mWmService.getDisplayAreaPolicyProvider().instantiate(
mWmService, this /* content */, this /* root */,
mImeWindowsContainer)
调用DisplayAreaPolicy中的Provider接口instantiate方法,去初始化一个DisplayArea层级结构
记住这边传递的参数,后面代码需要结合起来看
DisplayAreaPolicy.Provider
/**
* Provider for {@link DisplayAreaPolicy} instances.
*
* <p>By implementing this interface and overriding the
* {@code config_deviceSpecificDisplayAreaPolicyProvider}, a device-specific implementations
* of {@link DisplayAreaPolicy} can be supplied.
*/
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);
用来实例化一个DisplayAreaPolicy对象,这个对象应该建立起DisplayArea层级结构,实际走到的则是DisplayAreaPolicy.Provider的实现类DisplayAreaPolicy.DefaultProvider.instantiate方法
DisplayAreaPolicy.DefaultProvider
/** Provider for platform-default display area policy. */
static final class DefaultProvider implements DisplayAreaPolicy.Provider {
@Override
public DisplayAreaPolicy instantiate(WindowManagerService wmService,
DisplayContent content, RootDisplayArea root,
DisplayArea.Tokens imeContainer) {
//1.创建一个名为“DefaultTaskDisplayArea”的TaskDisplayArea,并将其添加到List中
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.
//2.创建HierarchyBuilder
final HierarchyBuilder rootHierarchy = new HierarchyBuilder(root);
// Set the essential containers (even if the display doesn't support IME).
//3.1添加ImeContainer到HierarchyBuilder
//3.2创建并保存默认TaskDisplayArea到HierarchyBuilder
rootHierarchy.setImeContainer(imeContainer).setTaskDisplayAreas(tdaList);
if (content.isTrusted()) {
// Only trusted display can have system decorations.
//4.为HierarchyBuilder添加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.
//5.生成DisplayArea层级结构
return new DisplayAreaPolicyBuilder().setRootHierarchy(rootHierarchy).build(wmService);
}
我们先了解下DisplayArea.Tokens的构造方法
Tokens(WindowManagerService wms, Type type, String name) {
this(wms, type, name, FEATURE_WINDOW_TOKENS);
}
FEATURE_WINDOW_TOKENS,容纳非activity窗口的DisplayArea,dump中对应着ImeContainer、WindowToken等
这里DefaultProvider实现了这个接口。
这个方法主要干了这几件事情:
1.初始化TaskDisplayArea
final TaskDisplayArea defaultTaskDisplayArea = new TaskDisplayArea(content, wmService,
"DefaultTaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER);
final List<TaskDisplayArea> tdaList = new ArrayList<>();
tdaList.add(defaultTaskDisplayArea);
FEATURE_DEFAULT_TASK_CONTAINER,容纳默认Task容器的DisplayArea,dump中正是以“DefaultTaskDisplayArea”命名
创建一个名为“DefaultTaskDisplayArea”的TaskDisplayArea,并将其添加到List中
2.创建HierarchyBuilder
final HierarchyBuilder rootHierarchy = new HierarchyBuilder(root);
HierarchyBuilder是什么?是用来定义在整个逻辑显示的根里面所需的一些Feature
HierarchyBuilder是在DisplayAreaPolicyBuilder中定义的
代码路径:frameworks/base/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
/**
* Builder to define {@link Feature} and {@link DisplayArea} hierarchy under a
* {@link RootDisplayArea}
*/
static class HierarchyBuilder {
private static final int LEAF_TYPE_TASK_CONTAINERS = 1;
private static final int LEAF_TYPE_IME_CONTAINERS = 2;
private static final int LEAF_TYPE_TOKENS = 0;
private final RootDisplayArea mRoot;
private final ArrayList<DisplayAreaPolicyBuilder.Feature> mFeatures = new ArrayList<>();
private final ArrayList<TaskDisplayArea> mTaskDisplayAreas = new ArrayList<>();
@Nullable
private DisplayArea.Tokens mImeContainer;
HierarchyBuilder(RootDisplayArea root) {
mRoot = root;
}
......
}
从代码中我们可以看出,HierarchyBuilder用来构建一个DisplayArea层级结构,该层级结构的根节点
其构造方法HierarchyBuilder(RootDisplayArea root)
传入的是RootDisplayArea的对象。
结合前面的configureSurfaces
方法中我们可以发现传入的是DisplayContent,即HierarchyBuilder以DisplayContent对象为根节点,生成一个DisplayArea层级结构。
3.1添加ImeContainer到HierarchyBuilder
rootHierarchy.setImeContainer(imeContainer).setTaskDisplayAreas(tdaList);
我们先看setImeContainer(imeContainer)
部分。其中参数imeContainer是DisplayArea.Tokens的对象。
在DisplayContent中DisplayAreaPolicy初始化时,传递了一个mImeWindowsContainer对应我们这里的imeContainer形参,其是在DisplayContent中定义并初始化的
代码路径:/frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
// Contains all IME window containers. Note that the z-ordering of the IME windows will depend
// on the IME target. We mainly have this container grouping so we can keep track of all the IME
// window containers together and move them in-sync if/when needed. We use a subclass of
// WindowContainer which is omitted from screen magnification, as the IME is never magnified.
// TODO(display-area): is "no magnification" in the comment still true?
private final ImeContainer mImeWindowsContainer = new ImeContainer(mWmService);
ImeContainer就是输入法的容器,其继承在DisplayContent中DisplayArea.Tokens
/**
* Container for IME windows.
*
* This has some special behaviors:
* - layers assignment is ignored except if setNeedsLayer() has been called before (and no
* layer has been assigned since), to facilitate assigning the layer from the IME target, or
* fall back if there is no target.
* - the container doesn't always participate in window traversal, according to
* {@link #skipImeWindowsDuringTraversal()}
*/
private static class ImeContainer extends DisplayArea.Tokens {
HierarchyBuilder的setImeContainer方法
代码路径:frameworks/base/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
private DisplayArea.Tokens mImeContainer;
/** Sets IME container as a child of this hierarchy root. */
HierarchyBuilder setImeContainer(DisplayArea.Tokens imeContainer) {
mImeContainer = imeContainer;
return this;
}
从代码中可以看出,就是将DisplayContent的mImeWindowsContainer保存到了HierarchyBuilder的mImeContainer成员变量中,后续创建DisplayArea层级结构时可以直接拿来使用。
3.2添加TaskDisplayArea到HierarchyBuilder
rootHierarchy.setImeContainer(imeContainer).setTaskDisplayAreas(tdaList);
这里我们看setTaskDisplayAreas(tdaList)
部分,第一步【1.初始化TaskDisplayArea】的时候,就已经把名为“DefaultTaskDisplayArea”的TaskDisplayArea,并将其添加到tdaList中,
private final ArrayList<TaskDisplayArea> mTaskDisplayAreas = new ArrayList<>();
/**
* Sets {@link TaskDisplayArea} that are children of this hierarchy root.
* {@link DisplayArea} group must have at least one {@link TaskDisplayArea}.
*/
HierarchyBuilder setTaskDisplayAreas(List<TaskDisplayArea> taskDisplayAreas) {
mTaskDisplayAreas.clear();
mTaskDisplayAreas.addAll(taskDisplayAreas);
return this;
}
虽然TaskDisplayArea是支持嵌套的,并且这里也采用了一个ArrayList来管理TaskDisplayArea,但是目前TaskDisplayArea只在这里被创建,即目前一个DisplayContent只有一个名为“DefaultTaskDisplayArea”的TaskDisplayArea。从dumpsys activity containers 中我们也可以看到,整个文件也只有一个“DefaultTaskDisplayArea”
4.为HierarchyBuilder添加Feature
// Only trusted display can have system decorations.
configureTrustedHierarchyBuilder(rootHierarchy, wmService, content);
configureTrustedHierarchyBuilder
这个方法就在DisplayAreaPolicy.DefaultProvider内部
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.
rootHierarchy.addFeature(new Feature.Builder(wmService.mPolicy, "WindowedMagnification",
FEATURE_WINDOWED_MAGNIFICATION)
.upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
.except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
// Make the DA dimmable so that the magnify window also mirrors the dim layer.
.setNewDisplayAreaSupplier(DisplayArea.Dimmable::new)
.build());
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());
}
}
从代码中可以看到五大的Feature:WindowedMagnification、HideDisplayCutout、OneHanded、FullscreenMagnification、ImePlaceholder,这些Feature其实也就是我们在dumpsys中看到那些,还有一些关键方法all()、and()、except()、upto()、build()等
在我们正式开始聊这个几个Feature添加之前,我们先来看看,Feature是怎么定义的
4.1 Feature的定义
从HierarchyBuilder的addFeature方法跟踪发现,Feature是在DisplayAreaPolicyBuilder中定义的
HierarchyBuilder addFeature(DisplayAreaPolicyBuilder.Feature feature) {
mFeatures.add(feature);
return this;
}
Feature的定义
代码路径/frameworks/base/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
/**
* A feature that requires {@link DisplayArea DisplayArea(s)}.
*/
static class Feature {
private final String mName;
private final int mId;
private final boolean[] mWindowLayers;
private final NewDisplayAreaSupplier mNewDisplayAreaSupplier;
private Feature(String name, int id, boolean[] windowLayers,
NewDisplayAreaSupplier newDisplayAreaSupplier) {
mName = name;
mId = id;
mWindowLayers = windowLayers;
mNewDisplayAreaSupplier = newDisplayAreaSupplier;
}
......
}
首先Feature代表的是DisplayArea的一个特征,可以根据Feature来对不同的DisplayArea进行划分。
- mName:这个Feature的名字,如上面的“WindowedMagnification”,“HideDisplayCutout”之类的,后续DisplayArea层级结构建立起来后,每个DisplayArea的名字用的就是当前DisplayArea对应的那个Feature的名字。
- mId:Feature的ID,如上面的FEATURE_WINDOWED_MAGNIFICATION和FEATURE_HIDE_DISPLAY_CUTOUT,虽说是Feature的ID,因为Feature又是DisplayArea的特征
- mWindowLayers:代表了这个DisplayArea可以包含哪些层级对应的窗口
- mNewDisplayAreaSupplier:只是一个接口,内部定义一个create方法。
关键是其Feature内部定义Builder类以及其build()方法
Feature.Builder和Feature.Builder.build()
static class Builder {
private final WindowManagerPolicy mPolicy;
private final String mName;
private final int mId;
private final boolean[] mLayers;
private NewDisplayAreaSupplier mNewDisplayAreaSupplier = DisplayArea::new;
private boolean mExcludeRoundedCorner = true;
/**
* Builds a new feature that applies to a set of window types as specified by the
* builder methods.
*
* <p>The set of types is updated iteratively in the order of the method invocations.
* For example, {@code all().except(TYPE_STATUS_BAR)} expresses that a feature should
* apply to all types except TYPE_STATUS_BAR.
*
* <p>The builder starts out with the feature not applying to any types.
*
* @param name the name of the feature.
* @param id of the feature. {@see Feature#getId}
*/
Builder(WindowManagerPolicy policy, String name, int id) {
mPolicy = policy;
mName = name;
mId = id;
mLayers = new boolean[mPolicy.getMaxWindowLayer() + 1];
}
......
Feature build() {
if (mExcludeRoundedCorner) {
// Always put the rounded corner layer to the top most layer.
mLayers[mPolicy.getMaxWindowLayer()] = false;
}
return new Feature(mName, mId, mLayers.clone(), mNewDisplayAreaSupplier);
}
通过一套适用于具体的窗口类型构建方法来构建新Feature
其中mLayers = new boolean[mPolicy.getMaxWindowLayer() + 1];
mPolicy.getMaxWindowLayer()返回的是窗口最大层数。
/**
* Returns the max window layer.
* <p>Note that the max window layer should be higher that the maximum value which reported
* by {@link #getWindowLayerFromTypeLw(int, boolean)} to contain rounded corner overlay.</p>
*
* @see WindowManager.LayoutParams#PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY
*/
default int getMaxWindowLayer() {
return 36;
}
1.代码中最大层数是36,这里+1,则也就是mLayers = new boolean[37]
,即窗口层级区间为[0,36]
2.在看看build()
方法中的 mLayers[mPolicy.getMaxWindowLayer()] = false;
,则表示mLayers[36]
= false,即第36层在build时会置为false(注:mExcludeRoundedCorner这个变量的值一直是true,没有改动)
3.return new Feature(mName, mId, mLayers.clone(), mNewDisplayAreaSupplier);
其中mLayers对应的就是Feature中的mWindowLayers,即mLayers.clone()
就是把Feature.mWindowLayer的值复制给Feature.Builder.mLayer。
下面我们来说说构建Feature的关键星魂
4.2 构建Feature的核心方法
以下代码均在DisplayAreaPolicyBuilder.Feature.Builder中
Feature.Builder第一星魂:all()
/**
* Set that the feature applies to all window types.
*/
Builder all() {
Arrays.fill(mLayers, true);
return this;
}
将mLayers数组中的所有元素都设置为true,表示当前DisplayArea可以包含所有类型的窗口。
简述,all()就是把所有类型窗口置为true(添加)
Feature.Builder第二星魂:and()
/**
* Set that the feature applies to the given window types.
*/
Builder and(int... types) {
for (int i = 0; i < types.length; i++) {
int type = types[i];
set(type, true);
}
return this;
}
先将传入的窗口类型先转换为对应的层级值,然后将mLayers数组中与该层级值对应的元素设置为true,表示该DisplayArea可以包含传入的窗口类型对应的窗口。
简述,and就是把你传入的所有参数(窗口类型)置为true(添加)
Feature.Builder第三星魂:except()
/**
* Set that the feature does not apply to the given window types.
*/
Builder except(int... types) {
for (int i = 0; i < types.length; i++) {
int type = types[i];
set(type, false);
}
return this;
}
先将传入的窗口类型先转换为对应的层级值,然后将mLayers数组中与该层级值对应的元素设置为false,表示该DisplayArea不再包含传入的窗口类型对应的窗口。
简述,except就是你传入的所有参数(窗口类型)置为false(不添加)
Feature.Builder第四星魂(必点):upTo()
/**
* Set that the feature applies window types that are layerd at or below the layer of
* the given window type.
*/
Builder upTo(int typeInclusive) {
final int max = layerFromType(typeInclusive, false);
for (int i = 0; i < max; i++) {
mLayers[i] = true;
}
set(typeInclusive, true);
return this;
}
先将传入的窗口类型先转换为对应的层级值,然后将mLayers数组中与该层级值对应的的元素之前的所有元素(包含该元素)设置为true,表示当前DisplayArea可以包含比传入的窗口类型层级值低的所有窗口。
简述,upTo把就是[0,typeInclusive]区间内的所有类型窗口置为true(添加)。
其中layerFromType方法非常重要,我们一起看看
private int layerFromType(int type, boolean internalWindows) {
return mPolicy.getWindowLayerFromTypeLw(type, internalWindows);
}
调用的了WindowManagerPolicy.getWindowLayerFromTypeLw方法
WindowManagerPolicy.getWindowLayerFromTypeLw
/**
* Returns the layer assignment for the window type. Allows you to control how different
* kinds of windows are ordered on-screen.
*
* @param type The type of window being assigned.
* @param canAddInternalSystemWindow If the owner window associated with the type we are
* evaluating can add internal system windows. I.e they have
* {@link Manifest.permission#INTERNAL_SYSTEM_WINDOW}. If true, alert window
* types {@link android.view.WindowManager.LayoutParams#isSystemAlertWindowType(int)}
* can be assigned layers greater than the layer for
* {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY} Else, their
* layers would be lesser.
* @return int An arbitrary integer used to order windows, with lower numbers below higher ones.
*/
default int getWindowLayerFromTypeLw(int type, boolean canAddInternalSystemWindow) {
return getWindowLayerFromTypeLw(type, canAddInternalSystemWindow,
false /* roundedCornerOverlay */);
}
/**
* Returns the layer assignment for the window type. Allows you to control how different
* kinds of windows are ordered on-screen.
*
* @param type The type of window being assigned.
* @param canAddInternalSystemWindow If the owner window associated with the type we are
* evaluating can add internal system windows. I.e they have
* {@link Manifest.permission#INTERNAL_SYSTEM_WINDOW}. If true, alert window
* types {@link android.view.WindowManager.LayoutParams#isSystemAlertWindowType(int)}
* can be assigned layers greater than the layer for
* {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY} Else, their
* layers would be lesser.
* @param roundedCornerOverlay