一,APP层
提供如下三种方法可以启动或者创建第二屏幕的显示。
1,APP创建时候设置displayid
应用层可以在activity创建的时候,调用setLaunchDisplayId(int) 来设置启动展示的id。
Display[] displays = displayManager.getDisplays();
Display targetDisplay = null;
for (Display display : displays) {
if(xxxxx) {
targetDisplay = display;
break;
}
break;
}
@override
protected void onCreate(Bundle saveInstanceState) {
super.onCreate(saveInstanceState);
setContentView(XXXXX);
setLaunchDisplayId(targetDisplay);
}
2, 在第二屏幕启动某个Activity
public static Display getSecondDisplayId(Context context){
DisplayManager displayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
Display[] displays = displayManager.getDisplays();
// Display[] arrayOfDisplay = displayManager.getDisplays(DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC);
return displays[1];
}
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchDisplayId(DisplayUtils.getSecondDisplayId(context).getDisplayId());
Intent intent = new Intent(context,CommonOptionDialog.class);
context.startActivity(intent,options.toBundle());
3, 通过创建第二屏Context方法,获取第二屏windowManager
public class DisplayUtils {
private Context mContext;
private DisplayManager mDisplayManager;
private ArrayList<Display> mAllDisplays = new ArrayList<>();
public DisplayUtils(Context context) {
mContext = context;
mDisplayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
}
public ArrayList<String> getCurrentDisplays() {
mAllDisplays.clear();
ArrayList<String> allDisplays = new ArrayList<String>();
Display[] displays = mDisplayManager.getDisplays();
for (Display display : displays) {
mAllDisplays.add(display);
allDisplays.add("Display " + display.getDisplayId() + " : " + display.getName());
}
return allDisplays;
}
//获取第二屏context
public Context getDisplayContext(int checked) {
int displayId = mAllDisplays.get(checked-1).getDisplayId();
if (displayId != DEFAULT_DISPLAY) {
Log.d("wangchong", "getDisplayContext not DEFAULT_DISPLAY, displayId = " + displayId);
return mContext.createDisplayContext(mAllDisplays.get(checked-1));
}
Log.d("wangchong", "getDisplayContext DEFAULT_DISPLAY");
return mContext;
}
}
未不同的屏幕创建不同的windowmanager代理
windowManager2 = displayUtils.getDisplayContext(2).getSystemService(WindowManager.class);
toast弹框选择屏幕参考
Display display = getDisplay(1);//获取ivi proup层屏幕的显示数据
displaycontext = getApplicationContext().createDisplayContext(display);//通过createDisplayContext创建displaycontex,通过displaycontext设置屏幕id
mToast = CustomToast.makeText(displaycontext, message,Toast.LENGTH_SHORT, 0, 0); //使用displaycontext创建toast
AlertDialog选择屏幕参考
Display display = getDisplay(1);//获取ivi proup层屏幕的显示数据
displaycontext = getApplicationContext().createDisplayContext(display);//通过createDisplayContext创建displaycontex,通过displaycontext设置屏幕id
AlertDialog = new AlertDialog(mContext,type);
二, DisplayManagerService
Android 中 对display 操作时, 从APP, DisplayManagerService, SurfaceFlinger到HWC通信都需要用到display id, 但是, 实际上,每个模块的display id 并不一致, 他们之间有一定的对应关系。
DisplayID
DisplayManagerService用于管理display 使用的id, 也是app 能够通过Display.getDisplayId() 得到的ID。
在android S之前的版本,该ID 是按display 加载的顺序确定的, 每加载一个display, id依次增一。 代码 :rameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java#assignDisplayIdLocked。
在Android S版本,DeviceStateToLayoutMap#loadLayoutsFromConfig函数加载固定displayId方案中的xml。
在启动DMS服务的阶段加载DisplayDeviceRepository,通知LogicalDisplayMapper display事件。然后调用onDisplayDeviceEventLocked。发生事件类型为DISPLAY_DEVICE_EVENT_ADDED的时机,对于LocalDisplayDevice有两处:
- DMS对象创建完成后,执行onStart(),注册LocalDisplayAdapter,从surfaceflinger获取所有的display,然后依次通知LogicalDisplayMapper有DISPLAY_DEVICE_EVENT_ADDED。
- 收到来自surfaceflinger的onHotplug事件,有display接入时,通知LogicalDisplayMapper有DISPLAY_DEVICE_EVENT_ADDED。
对于每一个INTERNAL屏(内置display)会分配两次displayId,最终会保留createDisplayLocked时分配的displayId,这样就实现Android S版本上内置display分配的displayId为固定值。具体逻辑在函数LogicalDisplayMapper#applyLayoutLocked中实现。
PhysicalDisplayID
SurfaceFlinger 中管理display 使用的ID, DisplayManagerService与SurfaceFlinger通信借助该ID。surfaceflinger中根据 mPhysicalDisplayId 找到HWC::Display, 通过HWC::Display中的mid 与hwc 通信, 其中的mId 为HWC给出, 即为下面的client_id.
android\frameworks\native\libs\ui\include\ui\DisplayId.h
client_id
HWC中定义的ID, 会传递给SurfaceFlinger, 与SurfaceFlinger 通信使用该ID。HWC按照可支持的display 数量和类型的定义:hardware/qcom/display/sdm/libs/hwc2/hwc_session.cpp中HWCSession::InitSupportedDisplaySlots看map_info.client_id赋值。 client_id 与 surfaceflinger 中HWC::Display中的mId 相同。 hwc_session中根据该client_id通过hwc_display_[client_id]找到相应的 HWCDisplay, 该HWCDisplay中记录了sdm_id, 对应底层的display id。
android\hardware\qcom\display\sdm\libs\hwc2\hwc_session.cpp# InitSupportedDisplaySlots
sdm_id
display driver传递给HWC的ID。
代码
android\hardware\qcom\display\sdm\libs\hwc2\hwc_session.cpp# CreatePrimaryDisplay
android\hardware\qcom\display\sdm\libs\core\drm\hw_info_drm.cpp# GetDisplaysStatus
android\vendor\qcom\proprietary\display\sde-drm\drm_connector.cpp
sdm_id就是resource中的connector。
inux的DRM架构,DRM是linux内核中负责与显卡交互的管理架构
drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res);
整体过程:
三, surfaceflinger
WindowManagerService 服务端,与绘制无关,控制窗口的状态、属性,如大小,位置等;
WindowManagerService 管理View 内容端,绘制,申请 Surface,Measure、Layout 和 Draw 过程。当为activity申请好了surface之后,每个进程就可以跟surfaceflinger通信了。
SurfaceFlinger 是一个独立的Service, 它接收所有Window的Surface作为输入,根据ZOrder, 透明度,大小,位置等参数,计算出每个Surface在最终合成图像中的位置,然后交由HWComposer或OpenGL生成最终的显示Buffer, 然后显示到显示设备上。
架构图:
数据流图
整体数据流程
四,HAL层 - HWC
hwc(hardware composer)是芯片厂商对android compoer的实现。高通的HWC架构如下:
五,驱动层 DRM
略。
六,openWFD软件架构
display驱动加载过程
七,display 配置
Display 驱动xml配置基础介绍
QNX/workspace/modules/bsp/apps/qnx_ap/boards/display/adp_star_sda8155/config/qcdisplaycfg_ADP_STAR_LA_MULTI_DISP_P03_TI_MST.xml
Display 驱动xml配置(QNX display id)
Display 驱动xml配置基础(android display id)
Display 驱动screen config配置基础