Android多屏
主要介绍cuttlefish Android环境中如何实现多屏和Android多屏一些介绍
1. 启动Android多屏
- 在cuttlefish模拟环境下启动3屏幕
HOME=$PWD ./bin/launch_cvd -daemon
-display0=width=1080,height=600,dpi=120
-display1=width=400,height=600,dpi=120
-display2=width=400,height=600,dpi=120
2. Dispaly&Layers
- display和ayers
Android多屏方案中:Drm驱动获取系统物理显示设备连接信息(如外接LVDS,HDMI等多个物理屏),hwcomposer向DRM获取显示信息并初始化
多个Dispaly,向SurfaceFlinger上报多个displayID,SurfaceFlinger根据Layer当中的layerStack成员,获知需要将该Layer绘制到具体哪一个
Display当中, 维护多displayID的input设备信息以及交互流程,hwc定期将layers合成到指定displayID并分别送至不同DisplayDevice显示。
- dumpsys SurfaceFlinger
1. 从SF角度查看Display/Layers信息:
有3个Display,每个Display下有多个Layer,如Display xx7201坐标(0,0)开始有400*467和400*600两个叠加Layer
2. Layer合成方式:
都是DEVICE,意为HWC进行Layer合成
3. 查看方式:
dumpsys SurfaceFlinger --display-id 查看持的display信息
- dumpsys display
4. 从HWC来看Display信息:
有4个Display,id分别为0,2,3,4
5. Dispaly描述:
Display 0为Built-in Screen,就是Android主屏
Display 2为HDMI Screen
Display 3为HDMI Screen,就是Android第三屏
Display 4为Cluster-App-VD
6. 为何SF看3个Dispaly,HWC来看4个Dispaly?
Display 2和Display 4叠加到一起显示到SF Display xx7201,也就是Android副屏
- screencap
7. 对指定dispalyID进行截屏
如截屏HWC display 4
screencap -d 4 -p /data/tmp/display4.png
- Am
- am start:将app启动到指定屏幕,重启
#参数--display指定屏幕, display 0,表示第一块屏幕; display 1,表示第2块屏幕。
#参数--user可以启动指定的用户,在多用户下有效,系统默认是--user 0
#如在display 2上启动百度网页
am start -a android.intent.action.VIEW -d http://www.baidu.com --display 2
- am display move-stack:将已启动app移动到另一屏,不重启
#display move-stack <STACK_ID> <DISPLAY_ID>
#将<STACK_ID>从当前显示移动到<DISPLAY_ID>
am stack list
#如将maps移动到display 3
am display move-stack 1000039 3
- 查找包名
- 查找所有包名,用于am启动MainActivity
dumpsys package | grep MainActivity
- 查找所有包名
pm list package
- 虚拟屏
1. 增加一个虚拟屏,非物理屏:如下图中间主屏中所示
开发者模式-> Simulate secondary displays
1. 从sf角度查看屏幕信息为虚拟屏
dumpsys SurfaceFlinger | grep DisplayDevice
3.多屏实现
如上图为cuttlefish Android基于drmvirgl的图形栈,以3屏启动为例:
1. 用户在launch_cvd时通过-display=传入3屏启动参数
2. launch_cvd将参数以--gpu-display形式传递给crosvm,进而拉起Android虚拟机
3. Android Drm初始化时,Virtio-gpu驱动向crosvm查询scanout,crosvm根据-display数目返回3个scanout给virtio-gpu驱动,
virtio-gpu驱动为每一个scanout初始化crtc/connector/decoder/encoder
4. hwcomposer通过libdrm查询到3个connector/crtc/decoder/encoder信息,并初始化3个display
- cuttlefish日志
1. Crosvm. launcher.log启动参数中向crosvm传递3个屏参数
--gpu=2D
--gpu-display=width=1080,height=600
--gpu-display=width=400,height=600
--gpu-display=width=400,height=600
2. Drm. Kernel.log内核日志:启动3屏scanout为3,启动1屏scanout为1
[drm] number of scanouts: 3
#logcat日志:3个display状态开启
VIRTUAL_DEVICE_DISPLAY_POWER_MODE_CHANGED display=0 mode=On
VIRTUAL_DEVICE_DISPLAY_POWER_MODE_CHANGED display=2 mode=On
VIRTUAL_DEVICE_DISPLAY_POWER_MODE_CHANGED display=1 mode=On
- Crosvm
# mod.rs::get_config(). 向virtio-gpu前端返回scanouts数目,个数由launch_cvd入参决定
fn get_config(&self) -> virtio_gpu_config {
let mut events_read = 0;
if self.config_event {
events_read |= VIRTIO_GPU_EVENT_DISPLAY;
}
virtio_gpu_config {
events_read: Le32::from(events_read),
events_clear: Le32::from(0),
num_scanouts: Le32::from(self.display_params.len() as u32), //scanouts个数由入参决定
num_capsets: Le32::from(num_capsets),
}
}
- virtio-gpu驱动
#virtio_gpu初始化. 从crosvm前端获取3个scanouts并初始化3套crtc/decoder/encoder/connector
virtio_gpu_probe
virtio_gpu_init //模块初始化
virtio_gpu_init_vq //初始化ctrlq,cursorq,收只有RESP,没有收的CMD
virtio_has_feature //检测设备支持特性
//日志打印:[drm]features: +virgl -edid +resource_blob +host_visible
//从crosvm读取num_scanouts配置,一个scanout可认为一个物理屏
virtio_cread_le(vgdev->vdev, struct virtio_gpu_config, num_scanouts, &num_scanouts);
virtio_gpu_modeset_init //mode初始化
drmm_mode_config_init //mode_configuration结构初始化
vgdev->ddev->mode_config.funcs //设置mode_config回调
vgdev->ddev->mode_config.helper_private
vgdev_output_init //对每一个scanout进行初始化,包含plane,crtc,connector,encoder
virtio_gpu_plane_init
drm_crtc_init_with_plane //为Atomic Mode Setting设置回调
drm_connector_init //Kernel Mode Setting设置回调
drm_simple_encoder_init //简单初始化encoder
drm_connector_attach_encoder //connector绑定到crtc
drm_connector_register
virtio_gpu_cmd_get_display_info //获取width/height/x/y
VIRTIO_GPU_GET_DISPLAY_INFO
2屏启动时,scanout为2 capset为2
1屏启动时,scanout为1 capset为2
- drm_hwcomposer
HwcLoader::load #HAL service,composer@2.4-service
HwcLoader::createHalWithAdapter
HwcLoader::openDeviceWithAdapter #dlopen打开HAL so
DrmHwcTwo::HookDevOpen #HAL so,创建DrmHwcTwo对象
DrmHwcTwo::Init #drm-hwc初始化
ResourceManager::Init #打开设备节点,热插拔回调
AddDrmDevice
DrmDevice::Init #Drm资源初始化
drmModeGetResources #从内核获取硬件信息
#有3个connector,代表3个物理屏
#目前1屏2 planes
#主屏:vendor.hwc.drm.primary_display_order设置
#如果找不到主屏,可用屏第一个为主屏
DrmHwcTwo::CreateDisplay #为每个屏创建Display
HwcDisplay::Init #设置后端
DrmDisplayCompositor::Init #为每个display初始化composer
vsync_worker_.Init
RegisterHotplugHandler #注册hotplug回调
2屏启动时,drm-hwc获取crtc,connector,encoder,display为2,plane为4
1屏启动时,drm-hwc获取crtc,connector,encoder,display为1,plane为2