背景:
在录屏或者投屏创建VirtualDisplay时候经常会用到一个flag属性:
简单说这个VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR功能就是在自己屏幕没有内容时候,会把另一个屏幕的内容进行镜像显示。即看到的如下状态:
上图既可以很好展示出这个虚拟屏幕的镜像功能,就是把主屏幕内容都复制一遍进行显示,即虚拟屏幕看到内容都和主屏幕内容一模一样,这个也就是录屏投屏必须会设置的VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR。
但VirtualDisplay也是可以进行自己内容的独立显示,比如可以把Activity单独搞到这Display上显示:
那么问题来了,请问系统是怎么做到的在有内容时候就不显示mirror主屏幕内容,没有内容就就只显示mirror内容呢?
原理调研方法步骤:
这里有了以前sf课程的知识基础,肯定知道一般各个layer要是显示在Display上,说明就是这个layer可以被Display包含,主要就是如下参数:
dumpsys SurfaceFlinger命令输出如下:
看看display的层级数据
同时在看看对应的每个layer也有对应的图层索引数据:
二者可以匹配那么就代表layer可以在这Display进行显示
看看镜像模式和自己内容显示时候,对于这个Display的layerStack有啥差异
显示自己内容时候如下:
显示镜像时候如下:
明显可以看到这里时候得layerStack居然变成0。
所以这里就可以有一个非常重要线索,那就是在VirtualDisplay没有显示内容和有显示内容时候会涉及一个layerStack的变化,这layerStack变化是不是可以通过打印啥的来抓取一下是哪里触发的这个layerStack的变化呢?所以下面就需要在相关变化layerStack的地方加入堆栈打印来追踪
堆栈追踪
这里上层一旦有Task移入VirtualDisplay,就会导致相关的layerStack进行变化,这里一般的最后触发到SurfaceFlinger体现了,说明会通过SurfaceControl相关方法。
加入相关堆栈后,操作把Task移入虚拟屏幕和移出虚拟屏幕看看打印情况如下:
移入Activity到Display:
移出:
看一看到确实有对这个layerStack进行修改在Task进行移入和移出时候
核心源码分析
核心源码经过追踪最后发现如下部分是核心:
首先通过判断是否有FLAG_OWN_CONTENT_ONLY,我们是VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR所以自然这地方的ownContent是false
final boolean ownContent = (info.flags & DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY) != 0;
所以核心会进入如下这个判断
这里的display.hasContentLocked标志就是返回当前Display是否有内容,如果没内容,那么就会吧dispkay变化成 device.getDisplayIdToMirrorLocked即这个displaydevice虚拟mirror的Display
而上面的hasContentLocked其实本质也是在通过遍历DisplayContent下面有没有可以显示Window决定的。
总结
VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR设置
1、会在DisplayContent的mApplySurfaceChangesTransaction中对Display是否有内容进行确定
2、一旦涉及了Display的上有内容到无内容的变化会调用setDisplayLayerStack来重新设置layerStack
3、在sf中layer会对layerStack进行匹配然后决定显示哪个Display
本文章更多详细代码和资料需要购买课程获取
hal+perfetto+surfaceflinger
https://mp.weixin.qq.com/s/LbVLnu1udqExHVKxd74ILg
私聊作者+v(androidframework007)
其他课程七件套专题:
点击这里
https://mp.weixin.qq.com/s/Qv8zjgQ0CkalKmvi8tMGaw