在上一篇Android R DisplayManagerService模块(1) 启动中说道,执行onStart()
方法时,将开始注册默认的Display适配器,本篇文章就该流程进行分析和总结。
1.创建屏幕适配器对象
在onStart()方法中,将会在android.display线程中执行registerDefaultDisplayAdapters()方法去注册默认DisplayAdapter:
// frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
private void registerDefaultDisplayAdapters() {
// Register default display adapters.
synchronized (mSyncRoot) {
// main display adapter
registerDisplayAdapterLocked(new LocalDisplayAdapter(
mSyncRoot, mContext, mHandler, mDisplayAdapterListener));
......
}
}
这里创建了LocalDisplayAdapter对象,并将该对象作为参数,执行registerDisplayAdapterLocked()方法:
// frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
private void registerDisplayAdapterLocked(DisplayAdapter adapter) {
mDisplayAdapters.add(adapter);
adapter.registerLocked();
}
首先,将LocalDisplayAdapter对象添加到mDisplayAdapters中。mDisplayAdapters是DMS中用来保存所有DisplayAdapter的列表。LocalDisplayAdapter的构造方法中没有多余的处理,基本属性的赋值都在其父类DisplayAdapter中:
// frameworks/base/services/core/java/com/android/server/display/DisplayAdapter.java
public DisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
Context context, Handler handler, Listener listener, String name) {
// DMS模块全局同步锁
mSyncRoot = syncRoot;
mContext = context;
// android.display线程的Handler
mHandler = handler;
// DislayApdater.Listener对象,用于回调DMS
mListener = listener;
mName = name;
}
2.创建物理屏对象
创建好适配器对象后,将执行registerLocked()方法,进入到DisplayAdapter中,和物理屏进行连接, LocalDisplayAdapter.registerLocked()如下:
@Override
// frameworks/base/services/core/java/com/android/server/display/LocalDisplayAdapter.java
public void registerLocked() {
super.registerLocked();
// 从SurfaceControl中获取物理DisplayId
for (long physicalDisplayId : SurfaceControl.getPhysicalDisplayIds()) {
// 连接Physical Display
tryConnectDisplayLocked(physicalDisplayId);
}
}
这个方法中,首先通过SurfaceControl获得所有的物理显示屏幕id,然后执行tryConnectDisplayLocked()方法,根据id创建对应的物理显示并和DMS进行连接:
// frameworks/base/services/core/java/com/android/server/display/LocalDisplayAdapter.java
private void tryConnectDisplayLocked(long physicalDisplayId) {
// 根据id获取Display的token
final IBinder displayToken = SurfaceControl.getPhysicalDisplayToken(physicalDisplayId);
if (displayToken != null) {
// 根据token获取所有的物理显示配置项
SurfaceControl.PhysicalDisplayInfo[] configs =
SurfaceControl.getDisplayConfigs(displayToken);
// 根据token获取正在使用的物理显示配置项
int activeConfig = SurfaceControl.getActiveConfig(displayToken);
......
// 根据id从mDevices数组中获取对应的LocalDisplayDevice
LocalDisplayDevice device = mDevices.get(physicalDisplayId);
if (device == null) {
// 创建LocalDisplayDevice
final boolean isInternal = mDevices.size() == 0;
device = new LocalDisplayDevice(displayToken, physicalDisplayId, info,
configs, activeConfig, configSpecs, colorModes, activeColorMode,
hdrCapabilities, isDefaultDisplay);
// 添加到mDevices中
mDevices.put(physicalDisplayId, device);
// 通知DMS添加DisplayDevice事件
sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
} else if (device.updatePhysicalDisplayInfoLocked(configs, activeConfig,
allowedConfigs, colorModes, activeColorMode)) { // 更新DisplayDevice
// 通知DMS更新DisplayDevice事件
sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
}
} else {
}
}
这里会从SurfaceControler中获取多个物理显示相关的配置属性。其中PhysicalDisplayInfo中封装了物理显示所有的属性配置,可通过如下命令查看其中的属性配置:
adb shell dumpsys display | grep mDisplayInfos
接下来,通过获取的id从mDevices数组中查找是否已存在对应的DisplayDevice对象,若不存在,将开始创建DisplayDevice对象,并在创建完成后,通知DMS创建对应的逻辑屏。
LocalDisplayDevice()如下:
// frameworks/base/services/core/java/com/android/server/display/LocalDisplayAdapter.java
LocalDisplayDevice(IBinder displayToken, long physicalDisplayId,
SurfaceControl.DisplayInfo info, SurfaceControl.DisplayConfig[] configs,
int activeConfigId, SurfaceControl.DesiredDisplayConfigSpecs configSpecs,
int[] colorModes, int activeColorMode, Display.HdrCapabilities hdrCapabilities,
boolean isDefaultDisplay) {
// 设置mDisplayAdapter、mDisplayToken、mUniqueId三个属性
super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + physicalDisplayId);
// 物理屏幕id
mPhysicalDisplayId = physicalDisplayId;
// 是否是默认屏
mIsDefaultDisplay = isDefaultDisplay;
mDisplayInfo = info;
// 更新物理屏配置
updateDisplayProperties(configs, activeConfigId, configSpecs, colorModes,
activeColorMode, hdrCapabilities);
mSidekickInternal = LocalServices.getService(SidekickInternal.class);
// 获取LightsManager对象
if (mIsDefaultDisplay) {
LightsManager lights = LocalServices.getService(LightsManager.class);
mBacklight = lights.getLight(LightsManager.LIGHT_ID_BACKLIGHT);
} else {
mBacklight = null;
}
......
// 创建亮度映射曲线
BackgroundThread.getHandler().sendMessage(PooledLambda.obtainMessage(
LocalDisplayDevice::loadDisplayConfigurationBrightnessMapping, this));
}
这里会对LocalDisplayDevice中的属性进行初始化,之后执行updatePhysicalDisplayInfoLocked()方法更新物理屏配置,最后获取设置背光的对象mBacklight,可以看出原生机制中,即使存在多个物理屏,mBacklight只有一个。
在updateDisplayProperties()方法中,会更新物理屏配置、色彩模式、HDR模式,分别对应三个方法。
创建完成LocalDisplayDevice对象后,接下来执行sendDisplayDeviceEventLocked()方法,通知DMS新添加DisplayDevice事件。
3.通知DMS完成物理屏创建
DisplayAdapter中是通过回调的方式来通知DMS的,回调接口类是DisplayAdapter.Listener,DMS中对其进行了实现,并在实例化LocalDisplayAdapter时作为参数传入。
下面接着看sendDisplayDeviceEventLocked()方法:
// frameworks/base/services/core/java/com/android/server/display/DisplayAdapter.java
protected final void sendDisplayDeviceEventLocked(
final DisplayDevice device, final int event) {
// 执行DisplayAdapter.Listener#onDisplayDeviceEvent()方法
mHandler.post(() -> mListener.onDisplayDeviceEvent(device, event));
}
这个mListener对象,正是DMS.DisplayAdapterListener类的对象:
// frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
private final class DisplayAdapterListener implements DisplayAdapter.Listener {
@Override
public void onDisplayDeviceEvent(DisplayDevice device, int event) {
switch (event) {
case DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED:
handleDisplayDeviceAdded(device);
break;
case DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED:
handleDisplayDeviceChanged(device);
break;
case DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED:
handleDisplayDeviceRemoved(device);
break;
}
}
@Override
public void onTraversalRequested() {
synchronized (mSyncRoot) {
scheduleTraversalLocked(false);
}
}
}
因此,LocalDisplayAdapter发起回调后,执行handleDisplayDeviceAdded()方法,其中直接调用handleDisplayDeviceAddedLocked()方法:
// frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
private void handleDisplayDeviceAddedLocked(DisplayDevice device) {
// 获取DisplayDeviceInfo对象
DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
......
// 添加到mDisplayDevices列表中
mDisplayDevices.add(device);
// 创建逻辑屏对象
LogicalDisplay display = addLogicalDisplayLocked(device);
// 更新display状态
Runnable work = updateDisplayStateLocked(device);
if (work != null) {
work.run();
}
// 向WMS发起窗口更新
scheduleTraversalLocked(false);
}
在这个方法中,首先会创建一个DisplayDeviceInfo对象,然后创建对应的逻辑屏对象,最后更新Display的状态信息,并向WMS发起窗口更新请求。
5.创建逻辑屏对象
DMS模块中通过addLogicalDisplayLocked()方法为物理屏创建对应的逻辑屏对象:
// frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
private LogicalDisplay addLogicalDisplayLocked(DisplayDevice device) {
DisplayDeviceInfo deviceInfo = device.getDisplayDeviceInfoLocked();
// 是否是默认Display
boolean isDefault = (deviceInfo.flags
& DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0;
......
// 分配逻辑屏id
final int displayId = assignDisplayIdLocked(isDefault, deviceInfo.address);
// 分配layer stack id
final int layerStack = assignLayerStackLocked(displayId);
// 创建LogicalDisplay对象
LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
// 根据和物理Display对应的DisplayDeviceInfo更新和LogicalDisplay对应的DisplayInfo
display.updateLocked(mDisplayDevices);
......
configureColorModeLocked(display, device);
// 添加到mLogicalDisplays列表中
mLogicalDisplays.put(displayId, display);
sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
return display;
}
该方法中:
- 首先,在创建逻辑显示对象前,会为即将创建的逻辑屏幕分配id和Layer Stack id;
- 然后,开始创建LogicalDisplay对象;
- 接下来,执行LogicalDisplay#updateLocked()方法,这个方法中,更新代表逻辑屏信息的DisplayInfo对象;
- 最后,执行sendDisplayEventLocked()方法,将逻辑屏添加事件通知给DMS模块之外的组件。
6. 更新Display状态
回到handleDisplayDeviceAddedLocked()中,LogicalDisplay创建相关流程执行后,接下开始更新DisplayDevice的状态:
private Runnable updateDisplayStateLocked(DisplayDevice device) {
// 获取DisplayDeviceInfo
DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
// 如果不带有FLAG_NEVER_BLANK标记,会请求更新DisplayDevice状态和亮度
if ((info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
return device.requestDisplayStateLocked(mGlobalDisplayState, mGlobalDisplayBrightness);
}
return null;
}
这个方法是DMS中更新Display状态和亮度的关键方法,对该方法的分析将在下一篇文章中进行。
整个过程时序图如下: