在Android R DisplayManagerService模块(1) 启动中说过,系统中每个物理屏都对应一个逻辑屏,DisplayInfo代表逻辑屏封装的信息对象,它并非固定不变的,当DisplayInfo对象发生变化后,WMS模块就会向DMS中发送通知,告知DMS模块进行相应处理。这篇文章就对逻辑屏信息发生变化后,DMS中的更新流程进行总结。
DMS中逻辑屏和物理屏配置参数的更新过程,和WMS模块有着密切的联系,因为DisplayInfo的更新发生在WMS中,当在更新Configuration时,会对DisplayInfo进行更新,如果发现发生了变化,如屏幕方向、屏幕大小、屏幕密度等属性发生变化,WMS会向DMS设置新的DisplayInfo。同时DMS中状态发生变化时,也会向WMS发出遍历请求,根据WMS中窗口状态设置对应的配置。
1.DisplayContent#updateDisplayAndOrientation()更新DisplayInfo
当WMS中状态发生变化后,在DisplayContent#updateDisplayAndOrientation()
方法中更新DisplayInfo对象:
// frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
private DisplayInfo updateDisplayAndOrientation(int uiMode, Configuration outConfig) {
......
final DisplayInfo overrideDisplayInfo = mShouldOverrideDisplayConfiguration
? mDisplayInfo : null;
mWmService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(mDisplayId,
overrideDisplayInfo);
......
return mDisplayInfo;
}
关于DisplayInfo更新的详细流程,会在WMS模块中进行分析。这里通过DMS#setDisplayInfoOverrideFromWindowManager()
方法,将DisplayInfo信息传递给了DMS,DMS中根据新的DisplayInfo更新逻辑屏信息。
2.DMS#setDisplayInfoOverrideFromWindowManagerInternal()更新逻辑屏
进入DMS后,在setDisplayInfoOverrideFromWindowManagerInternal()方法中进行处理:
// frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
private void setDisplayInfoOverrideFromWindowManagerInternal(
int displayId, DisplayInfo info) {
synchronized (mSyncRoot) {
// 根据Display Id获取逻辑屏
LogicalDisplay display = mLogicalDisplays.get(displayId);
if (display != null) {
if (display.setDisplayInfoOverrideFromWindowManagerLocked(info)) {
// 更新逻辑屏
handleLogicalDisplayChanged(displayId, display);
// 向WMS发起窗口遍历
scheduleTraversalLocked(false);
}
}
}
}
该方法中:
- 首先根据displayId得到对应的逻辑屏;
- 然后调用
LogicalDisplay#setDisplayInfoOverrideFromWindowManagerLocked()
方法更新DMS中DisplayInfo变量; - 如果逻辑屏信息发生了更新,会回调
onDisplayChanged()
方法,并向WMS发起窗口遍历;
下面来逐一进行分析。
2.1.LogicalDisplay#setDisplayInfoOverrideFromWindowManagerLocked()`更新DMS中DisplayInfo变量
// frameworks/base/services/core/java/com/android/server/display/LogicalDisplay.java
public boolean setDisplayInfoOverrideFromWindowManagerLocked(DisplayInfo info) {
if (info != null) {
if (mOverrideDisplayInfo == null) {
mOverrideDisplayInfo = new DisplayInfo(info);
mInfo.set(null);
return true;
// 说明mOverrideDisplayInfo和info不同
} else if (!mOverrideDisplayInfo.equals(info)) {
// 更新mOverrideDisplayInfo
mOverrideDisplayInfo.copyFrom(info);
// 置空mInfo,等待更新
mInfo.set(null);
return true;
}
} else if (mOverrideDisplayInfo != null) {
mOverrideDisplayInfo = null;
mInfo.set(null);
return true;
}
return false;
}
在该方法中,如果mOverrideDisplayInfo和info不一致,说明逻辑屏发生了变化,则会将info更新到mOverrideDisplayInfo中,并置空mInfo,同时返回true,进行逻辑屏的更新。
mInfo
是一个DisplayInfoProxy对象,它内部持有一个DisplayInfo对象,这个对象就是当前系统的全局逻辑屏信息,对mInfo进行更新时,首先需要将它置空。
mOverrideDisplayInfo
表示来自WMS中的DisplayInfo对象,也即实际的DisplayInfo对象,在更新mInfo对象时,将使用该对象进行更新。
2.2.sendDisplayEventLocked()发起DisplayListener#onDisplayChanged()回调
如果DisplayInfo发生变化,接下来执行handleLogicalDisplayChanged()
方法,该方法中调用sendDisplayEventLocked()方法,最终发起了DisplayManager.DisplayListener#onDisplayChanged()
方法的回调:
// frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
private void sendDisplayEventLocked(int displayId, int event) {
Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT, displayId, event);
mHandler.sendMessage(msg);
}
2.3.scheduleTraversalLocked()向WMS请求发起窗口遍历
在方法中,将向WMS发起遍历窗口请求:
private void scheduleTraversalLocked(boolean inTraversal) {
if (!mPendingTraversal && mWindowManagerInternal != null) {
// 发起标记
mPendingTraversal = true;
if (!inTraversal) {
mHandler.sendEmptyMessage(MSG_REQUEST_TRAVERSAL);
}
}
}
在Handler中,直接调用WMS.requestTraversalFromDisplayManager()向WMS中发起更新请求,在android.display
线程进行:
// frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
private final class DisplayManagerHandler extends Handler {
public DisplayManagerHandler(Looper looper) {
super(looper, null, true /*async*/);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
......
case MSG_REQUEST_TRAVERSAL:
// 向WMS发起遍历请求
mWindowManagerInternal.requestTraversalFromDisplayManager();
break;
......
}
}
}
2.4.DMS#getDisplayInfo()完成DisplayInfo更新并返回给其他组件
当mInfo被置空后,将等待被再次更新,而再次更新可以由应用发起,也可以由system_server发起,DMS提供了getDisplayInfo()
方法,可以让应用端通过Binder调用触发更新,比如在方向旋转时,应用所在Activity在执行onConfiguration()方法之前,会先进行DisplayInfo的更新:
// frameworks/base/core/java/android/app/ActivityThread.java
private void updateDisplayInfoLocked() {
DisplayInfo newInfo = mGlobal.getDisplayInfo(mDisplayId);
......
}
DMS#getDisplayInfoLocked()方法如下:
// frameworks/base/services/core/java/com/android/server/display/LogicalDisplay.java
public DisplayInfo getDisplayInfoLocked() {
if (mInfo.get() == null) {
DisplayInfo info = new DisplayInfo();
info.copyFrom(mBaseDisplayInfo);
if (mOverrideDisplayInfo != null) {
info.appWidth = mOverrideDisplayInfo.appWidth;
info.appHeight = mOverrideDisplayInfo.appHeight;
info.smallestNominalAppWidth = mOverrideDisplayInfo.smallestNominalAppWidth;
info.smallestNominalAppHeight = mOverrideDisplayInfo.smallestNominalAppHeight;
info.largestNominalAppWidth = mOverrideDisplayInfo.largestNominalAppWidth;
info.largestNominalAppHeight = mOverrideDisplayInfo.largestNominalAppHeight;
info.logicalWidth = mOverrideDisplayInfo.logicalWidth;
info.logicalHeight = mOverrideDisplayInfo.logicalHeight;
info.rotation = mOverrideDisplayInfo.rotation;
info.displayCutout = mOverrideDisplayInfo.displayCutout;
info.logicalDensityDpi = mOverrideDisplayInfo.logicalDensityDpi;
info.physicalXDpi = mOverrideDisplayInfo.physicalXDpi;
info.physicalYDpi = mOverrideDisplayInfo.physicalYDpi;
}
mInfo.set(info);
}
return mInfo.get();
}
在该方法中,如果mInfo对象不为null,则直接返回,否则从mOverrideDisplayInfo对象中更新mInfo对象,完成逻辑屏信息的更新。
此时,DMS中第一阶段的流程执行完毕。在这一阶段,主要干了三件事:
- 1.更新mOverrideDisplayInfo和mInfo对象;
- 2.回调DisplayListener#onDisplayChanged()方法;
- 3.向WMS发起遍历窗口请求进行窗口摆放工作;
接下来看下在WMS对WindowState的遍历过程中,和DMS做了哪些交互。
在WMS遍历WindowState进行窗口安置的过程中,和DMS有两处交互:
- 一是在遍历过程中,会获取窗口所携带控制显示方式的首选参数,并由DMS#setDisplayProperties()方法设置给DMS;
- 二是在完成所有窗口Surface的安置后,通过
DisplayManagerInternal#performTraversal()
通知DMS,j进行物理屏相关的配置更新,如界面显示的Layer Stack、区域大小......
下面逐一来看。
3.DMS#setDisplayProperties()设置窗口首选参数
WMS中安置WindowState的Surface的详细流程,会在WMS模块中进行分析。这里只看和这部分内容相关的流程。在DisplayContent中的函数接口对象mApplySurfaceChangesTransaction
中,会遍历所有的窗口,并将当前显示窗口中携带的首选参数保持到mTmpApplySurfaceChangesTransactionState对象中:
// frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
private final Consumer<WindowState> mApplySurfaceChangesTransaction = w -> {
final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
final boolean obscuredChanged = w.mObscured !=
mTmpApplySurfaceChangesTransactionState.obscured;
final RootWindowContainer root = mWmService.mRoot;
if (!mTmpApplySurfaceChangesTransactionState.obscured) {
final boolean isDisplayed = w.isDisplayedLw();
if (isDisplayed && w.isObscuringDisplay()) {
root.mObscuringWindow = w;
mTmpApplySurfaceChangesTransactionState.obscured = true;
}
// 逻辑屏上是否有内容显示,用于控制多屏镜像
final boolean displayHasContent = root.handleNotObscuredLocked(w,
mTmpApplySurfaceChangesTransactionState.obscured,
mTmpApplySurfaceChangesTransactionState.syswin);
if (!mTmpApplySurfaceChangesTransactionState.displayHasContent
&& !getDisplayPolicy().isWindowExcludedFromContent(w)) {
mTmpApplySurfaceChangesTransactionState.displayHasContent |= displayHasContent;
}
if (w.mHasSurface && isDisplayed) {
final int type = w.mAttrs.type;
// 设置首选帧率
if (mTmpApplySurfaceChangesTransactionState.preferredRefreshRate == 0
&& w.mAttrs.preferredRefreshRate != 0) {
mTmpApplySurfaceChangesTransactionState.preferredRefreshRate
= w.mAttrs.preferredRefreshRate;
}
// 设置preferMinimalPostProcessing
mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing
|= w.mAttrs.preferMinimalPostProcessing;
final int preferredModeId = getDisplayPolicy().getRefreshRatePolicy()
.getPreferredModeId(w);
// 设置preferredModeId
if (mTmpApplySurfaceChangesTransactionState.preferredModeId == 0
&& preferredModeId != 0) {
mTmpApplySurfaceChangesTransactionState.preferredModeId = preferredModeId;
}
}
}
......
};
遍历完成后,将值保存在mTmpApplySurfaceChangesTransactionState中,并在最后向DMS发出请求,设置显示参数,如刷新率、宽高、是否镜像......
// frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
void applySurfaceChangesTransaction() {
mTmpApplySurfaceChangesTransactionState.reset();
......
mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
mLastHasContent,
mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
mTmpApplySurfaceChangesTransactionState.preferredModeId,
mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing,
true /* inTraversal, must call performTraversalInTrans... below */);
}
这里几个参数含义如下:
- hasContent表示是否逻辑屏上是否有显示内容,用于控制屏幕的镜像;
- preferredRefreshRate表示最顶层窗口携带的首选刷新率;
- preferredModeId表示最顶层窗口携带的首选模式;
- preferMinimalPostProcessing表示最小的Post-Processing, 这是一个显示专业术语。
4.DMS#performTraversal()更新屏幕配置
当所有窗口安置完毕后,WMS中通过DMS#performTraversal()方法和mDisplayTransaction对象,进行DMS中的事务提交:
// frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
private void applySurfaceChangesTransaction() {
......
//遍历每个DisplayContent,对每个Display中的Surface进行Placement操作
final int count = mChildren.size();
for (int j = 0; j < count; ++j) {
final DisplayContent dc = mChildren.get(j);
dc.applySurfaceChangesTransaction();
}
// 通知DMS进行Display侧的属性调整
mWmService.mDisplayManagerInternal.performTraversal(mDisplayTransaction);
// 将mDisplayTransaction合并到sGlobalTransaction上
SurfaceControl.mergeToGlobalTransaction(mDisplayTransaction);
}
进入DMS后:
// frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
void performTraversalInternal(SurfaceControl.Transaction t) {
synchronized (mSyncRoot) {
// 只有mPendingTraversal才会进行更新
if (!mPendingTraversal) {
return;
}
// 重新置为false
mPendingTraversal = false;
performTraversalLocked(t);
}
for (DisplayTransactionListener listener : mDisplayTransactionListeners) {
listener.onDisplayTransaction(t);
}
}
可以看到,只有mPendingTraversal
才会进行更新,而mPendingTraversal恰好在scheduleTraversalLocked()方法中设置为true,接着执行performTraversalLocked()方法,这个方法中将逐步完成DMS的更新:
// frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
private void performTraversalLocked(SurfaceControl.Transaction t) {
// 清除DisplayViewport列表
clearViewportsLocked();
// 遍历所有physical display
final int count = mDisplayDevices.size();
for (int i = 0; i < count; i++) {
DisplayDevice device = mDisplayDevices.get(i);
// 进行物理屏配置更新
configureDisplayLocked(t, device);
device.performTraversalLocked(t);
}
// Viewport发生变化时,通知IMS
if (mInputManagerInternal != null) {
mHandler.sendEmptyMessage(MSG_UPDATE_VIEWPORT);
}
}
该方法中,会对每一个physical display进行配置更新,最后如果DisplayViewport发生变化,通知IMS。
4.1.configureDisplayLocked()
该方法如下:
// frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
private void configureDisplayLocked(SurfaceControl.Transaction t, DisplayDevice device) {
// 获取物理屏对应的DisplayDeviceInfo对象
final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
// 是否允许将该display内容镜像到其他Display上,true表示不允许
final boolean ownContent = (info.flags & DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY) != 0;
// 获取该physical display对应的logical display对象
LogicalDisplay display = findLogicalDisplayForDeviceLocked(device);
// 如果该物理屏允许镜像,根据display.hasContentLocked()确定镜像源
if (!ownContent) {
if (display != null && !display.hasContentLocked()) {
display = mLogicalDisplays.get(device.getDisplayIdToMirrorLocked());
}
if (display == null) {
display = mLogicalDisplays.get(Display.DEFAULT_DISPLAY);
}
}
// 配置logical display
display.configureDisplayLocked(t, device, info.state == Display.STATE_OFF);
// 更新viewport type
final Optional<Integer> viewportType = getViewportType(info);
// 填充viewport
populateViewportLocked(viewportType, display.getDisplayIdLocked(), device, info.uniqueId);
}
这个方法中,将完成对逻辑屏、物理屏以及viewport的重新配置。下面逐步进行分析。
4.2.configureDisplayLocked()更新物理屏配置
该方法如下:
// frameworks/base/services/core/java/com/android/server/display/LogicalDisplay.java
public void configureDisplayLocked(SurfaceControl.Transaction t,
DisplayDevice device,
boolean isBlanked) {
// 设置layer stack id
device.setLayerStackLocked(t, isBlanked ? BLANK_LAYER_STACK : mLayerStack);
// 设置色彩模式
......
// 获取逻辑屏对应DisplayInfo对象
final DisplayInfo displayInfo = getDisplayInfoLocked();
// 获取物理屏对应DisplayDeviceInfo对象
final DisplayDeviceInfo displayDeviceInfo = device.getDisplayDeviceInfoLocked();
// 设置mTempLayerStackRect区域,这个Rect表示在物理屏上要显示的逻辑屏区域
mTempLayerStackRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
// 确定方向
int orientation = Surface.ROTATION_0;
if ((displayDeviceInfo.flags & DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT) != 0) {
orientation = displayInfo.rotation;
}
orientation = (orientation + displayDeviceInfo.rotation) % 4;
// 根据显示方向,确定
boolean rotated = (orientation == Surface.ROTATION_90
|| orientation == Surface.ROTATION_270);
// 获取physical display 宽高
int physWidth = rotated ? displayDeviceInfo.height : displayDeviceInfo.width;
int physHeight = rotated ? displayDeviceInfo.width : displayDeviceInfo.height;
// 获取被隐藏区域
Rect maskingInsets = getMaskingInsets(displayDeviceInfo);
InsetUtils.rotateInsets(maskingInsets, orientation);
// physical 尺寸减去隐藏区域的宽高
physWidth -= maskingInsets.left + maskingInsets.right;
physHeight -= maskingInsets.top + maskingInsets.bottom;
int displayRectWidth, displayRectHeight;
if ((displayInfo.flags & Display.FLAG_SCALING_DISABLED) != 0 || mDisplayScalingDisabled) {
displayRectWidth = displayInfo.logicalWidth;
displayRectHeight = displayInfo.logicalHeight;
} else if (physWidth * displayInfo.logicalHeight
< physHeight * displayInfo.logicalWidth) {
// Letter box.信封模式
displayRectWidth = physWidth;
displayRectHeight = displayInfo.logicalHeight * physWidth / displayInfo.logicalWidth;
} else {
// Pillar box. 邮桶模式(两黑边)
displayRectWidth = displayInfo.logicalWidth * physHeight / displayInfo.logicalHeight;
displayRectHeight = physHeight;
}
int displayRectTop = (physHeight - displayRectHeight) / 2;
int displayRectLeft = (physWidth - displayRectWidth) / 2;
// 设置mTempDisplayRect区域
mTempDisplayRect.set(displayRectLeft, displayRectTop,
displayRectLeft + displayRectWidth, displayRectTop + displayRectHeight);
// 设置偏移量
mTempDisplayRect.offset(maskingInsets.left, maskingInsets.top);
// 应用偏移量mDisplayOffsetX和mDisplayOffsetY
if (orientation == Surface.ROTATION_0) {
mTempDisplayRect.offset(mDisplayOffsetX, mDisplayOffsetY);
} else if (orientation == Surface.ROTATION_90) {
mTempDisplayRect.offset(mDisplayOffsetY, -mDisplayOffsetX);
} else if (orientation == Surface.ROTATION_180) {
mTempDisplayRect.offset(-mDisplayOffsetX, -mDisplayOffsetY);
} else { // Surface.ROTATION_270
mTempDisplayRect.offset(-mDisplayOffsetY, mDisplayOffsetX);
}
// 设置physical display投影
device.setProjectionLocked(t, orientation, mTempLayerStackRect, mTempDisplayRect);
}
这个方法中,主要做了以下三件事:
- 首先,设置Layer Stack Id;
- 然后,确定mTempLayerStackRect和mTempDisplayRect两区域;
- 最后,向SurfaceFlinger中设置Display投影;
4.3.DisplayDevice#setLayerStackLocked()设置Layer Stack
Layer Stack Id表示每个物理屏在SurfaceFlinger中对应的layer stack,在该Display上显示的layer,都处于该Layer Stack中。在创建逻辑屏时,会指定一个layer stack id, 当灭屏时,Layer Stack Id将被设置为-1, 不会显示任何内容:
// frameworks/base/services/core/java/com/android/server/display/DisplayDevice.java
public final void setLayerStackLocked(SurfaceControl.Transaction t, int layerStack) {
if (mCurrentLayerStack != layerStack) {
// 表示当前layer stack id
mCurrentLayerStack = layerStack;
// 设置
t.setDisplayLayerStack(mDisplayToken, layerStack);
}
}
4.4.DisplayDevice#setProjectionLocked()设置投影矩阵
接下来,会确定mTempLayerStackRect和mTempDisplayRect这两对象,mTempLayerStackRect表示逻辑屏大小区域,mTempDisplayRect表示物理屏上显示区域,通过SurfaceControl.Transaction#setDisplayLayerStack()将这两区域传给SurfaceFlinger后,计算得出最终的投影矩阵:
//
public final void setProjectionLocked(SurfaceControl.Transaction t, int orientation,
Rect layerStackRect, Rect displayRect) {
if (mCurrentOrientation != orientation
|| mCurrentLayerStackRect == null
|| !mCurrentLayerStackRect.equals(layerStackRect)
|| mCurrentDisplayRect == null
|| !mCurrentDisplayRect.equals(displayRect)) {
// 当前屏幕方向
mCurrentOrientation = orientation;
// 更新逻辑显示区域和对应的物理显示区域
......
mCurrentLayerStackRect.set(layerStackRect);
......
mCurrentDisplayRect.set(displayRect);
// 传递给SurfaceControl中计算投影矩阵
t.setDisplayProjection(mDisplayToken,
orientation, layerStackRect, displayRect);
}
}
在这个方法中,会将mTempLayerStackRect和mTempDisplayRect更新给全局变量mCurrentLayerStackRect和mCurrentDisplayRect,将在映射ViewPort时发挥作用。
至此,LogicalDisplay#configureDisplayLocked()方法执行完毕,回到DMS#configureDisplayLocked()方法中,接下来将开始进行viewport的填充。
4.5.更新ViewPort
ViewPort表示物理屏和逻辑屏之间的对应关系,Input模块中,用ViewPort来将物理屏的touch事件坐标转换为逻辑屏的坐标。所以,每次进行逻辑屏和物理屏配置时,都会重新对view port进行填充,并发送给IMS模块。
每次进行performTraversalLocked()时,首先会清空当前ViewPort列表:
// frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
private void clearViewportsLocked() {
mViewports.clear();
}
在执行完LogicalDisplay#configureDisplayLocked()
方法后,物理屏的mCurrentOrientation、mCurrentDisplayRect都完成了更新,接下来通过DMS#getViewportType()
方法,根据物理屏属性确定对应物理屏新的ViewPort类型:
// frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
private Optional<Integer> getViewportType(DisplayDeviceInfo info) {
// 内置屏,Viewport类型为VIEWPORT_INTERNAL
if ((info.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0) {
return Optional.of(VIEWPORT_INTERNAL);
// 设置了TOUCH_EXTERNAL标记,表示touch事件来自外部,Viewport类型为VIEWPORT_INTERNAL
} else if (info.touch == DisplayDeviceInfo.TOUCH_EXTERNAL) {
return Optional.of(VIEWPORT_EXTERNAL);
} else if (info.touch == DisplayDeviceInfo.TOUCH_VIRTUAL
&& !TextUtils.isEmpty(info.uniqueId)) {
return Optional.of(VIEWPORT_VIRTUAL);
} else {
}
return Optional.empty();
}
4.6.populateViewportLocked()填充ViewPort
接下来通过DMS#populateViewportLocked()
方法,对ViewPort进行填充:
// frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
private void populateViewportLocked(int viewportType, int displayId, DisplayDevice device,
DisplayDeviceInfo info) {
final DisplayViewport viewport = getViewportLocked(viewportType, info.uniqueId);
// 进入对应物理屏中进行填充
device.populateViewportLocked(viewport);
// 表示有效
viewport.valid = true;
viewport.displayId = displayId;
// 根据Display state确定viewport是否处于active状态
viewport.isActive = Display.isActiveState(info.state);
}
进入到对应的物理屏中进行填充:
// frameworks/base/services/core/java/com/android/server/display/DisplayDevice.java
public final void populateViewportLocked(DisplayViewport viewport) {
// 设置方向
viewport.orientation = mCurrentOrientation;
// 设置逻辑屏区域
if (mCurrentLayerStackRect != null) {
viewport.logicalFrame.set(mCurrentLayerStackRect);
} else {
viewport.logicalFrame.setEmpty();
}
// 设置物理屏区域
if (mCurrentDisplayRect != null) {
viewport.physicalFrame.set(mCurrentDisplayRect);
} else {
viewport.physicalFrame.setEmpty();
}
// 是否发生旋转
boolean isRotated = (mCurrentOrientation == Surface.ROTATION_90
|| mCurrentOrientation == Surface.ROTATION_270);
// 获取物理屏信息
DisplayDeviceInfo info = getDisplayDeviceInfoLocked();
// 表示物理屏宽高
viewport.deviceWidth = isRotated ? info.height : info.width;
viewport.deviceHeight = isRotated ? info.width : info.height;
// 物理屏id
viewport.uniqueId = info.uniqueId;
// 物理屏固定的物理端口
if (info.address instanceof DisplayAddress.Physical) {
viewport.physicalPort = ((DisplayAddress.Physical) info.address).getPort();
} else {
viewport.physicalPort = null;
}
}
可以看到,对ViewPort的填充,就是将逻辑屏的显示区域宽高、物理屏的显示区域宽高、以及物理屏的DisplayDeviceInfo对象中携带的信息传递给ViewPort对象。(viewport.physicalPort对象非常重要,在后面会看到还有一种静态路由方式)。
4.7.通知IMS更新ViewPort
在DMS#performTraversalLocked()
方法的最后,通过Handler发送了一个MSG_UPDATE_VIEWPORT,会将变化后的viewport通知给IMS:
// frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
private final class DisplayManagerHandler extends Handler {
......
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
......
case MSG_UPDATE_VIEWPORT: {
final boolean changed;
synchronized (mSyncRoot) {
changed = !mTempViewports.equals(mViewports);
if (changed) {
mTempViewports.clear();
for (DisplayViewport d : mViewports) {
mTempViewports.add(d.makeCopy());
}
}
}
if (changed) {
mInputManagerInternal.setDisplayViewports(mTempViewports);
}
break;
}
......
}
}
}
到这里,整个DMS中的更新完成,最终随着WMS中事务的提交,完成更新后的显示。
主要过程中主要流程时序图如下: