背景近来手机界最火爆的话题莫过于豆包手机助手https://o.doubao.com/很多博主拿到豆包手机后也开始对豆包手机助手进行一系列的实现原理调研和猜想比如调研时候就涉及几个我们系统fw和应用开发中常用的一些权限CAPTURE_SECURE_VIDEO_OUTPUT录取屏幕数据INJECT_EVENTS注入相关输入事件。其实这两个权限在我们投屏专题和input专题课程都有介绍过不过课程讲解抓取屏幕权限那时候是CAPTURE_VIDEO_OUTPUT权限而豆包用的是CAPTURE_SECURE_VIDEO_OUTPUT多了一个SECURE那么这里的权限CAPTURE_SECURE_VIDEO_OUTPUT到底能用来干什么呢?下面来详细剖析一下这个CAPTURE_SECURE_VIDEO_OUTPUT权限。CAPTURE_SECURE_VIDEO_OUTPUT权限介绍CAPTURE_SECURE_VIDEO_OUTPUT权限添加uses-permissionandroid:nameandroid.permission.CAPTURE_SECURE_VIDEO_OUTPUT/对应权限的定义frameworks/base/core/res/AndroidManifest.xml!-- Allows an application to capture video output. pNot for use by third-party applications./p hide removed --permissionandroid:nameandroid.permission.CAPTURE_VIDEO_OUTPUTandroid:protectionLevelsignature/!-- Allows an application to capture secure video output. pNot for use by third-party applications./p hide removed --permissionandroid:nameandroid.permission.CAPTURE_SECURE_VIDEO_OUTPUTandroid:protectionLevelsignature/官方注释解释允许一个app可以抓取安全级别的视频内容这个权限不是给第三方app用的是需要系统签名的权限保护级别也是android:protectionLevel“signature”。其实只看这个权限了解的话大家还是会感觉比较空只能理解到字面意思就是可以抓取安全级别的视频内容所以要全面了解只看这一点注释还远远不够。首先疑问就是什么是安全级别内容有现实的案例来说明么开发过程中哪些画面界面就是要设置这个安全级别内容。安全Activity画面如何设置当一个窗口被标记为 FLAG_SECURE 后系统会认为它包含敏感内容从而阻止或限制以下行为禁止常规截屏包括adb shell screencap命令和大多数手机自带的截屏功能。禁止录屏包括系统录屏工具和大多数录屏App。防止内容出现在不安全的显示输出上例如通过MediaProjection API投屏、录屏或显示在非受信的外部显示器上。比如涉及到场景为密码输入手势输入银行App登录、账户总览、交易密码输入、支付确认、付款二维码/条形码包含金额实际案例这里就以手机的手势设置画面来进行代码剖析。按如下路径进行操作设置app —》 安全与隐私 —》设置锁屏 —》 手势密码设置那么看看这个手势密码设置画面的详细情况Activity画面在onCreate时候就把Activity设置成了FLAG_SECURE下面看看这个FLAG_SECUREframeworks/base/core/java/android/view/WindowManager.java/** Window flag: treat the content of the window as secure, preventing * it from appearing in screenshots or from being viewed on non-secure * displays. * * pSee {link android.view.Display#FLAG_SECURE} for more details about * secure surfaces and secure displays. */publicstaticfinalintFLAG_SECURE0x00002000;注释也比较详细可以看出主要是把window的内容变成安全的防止window内容展示在截图或者不安全的display中比如常见投屏录屏的虚拟屏。dumpsys SurfaceFlinger后查看相关输出如下可以看到这里的ChooseLockPattern的Activity对应Layer实际上isSecure为true如果没有额外特别设置则一般 isSecurefalse。* Layer 0x735dc3562330(2ed9211 com.android.settings/com.android.settings.SubSettings#355)isSecurefalsegeomUsesSourceCropfalsegeomBufferUsesDisplayInverseTransformfalse geomLayerTransform(ROT_0)(IDENTITY)那么上面也一直强调说不安全的display的那么display的是否安全又是靠啥标识和控制呢这里接下来就需要看Display创建时候的对应flag了。VIRTUAL_DISPLAY_FLAG_SECURE的Flag介绍创建虚拟屏幕这块已经在投屏课程有深入讲解也有对这些flag进行剖析这里主要看看VIRTUAL_DISPLAY_FLAG_SECURE这个flag的讲解。frameworks/base/core/java/android/hardware/display/DisplayManager.java/** * Virtual display flag: Create a secure display. * * h3Secure virtual displays/h3 * p * When this flag is set, the virtual display is considered secure as defined * by the {link Display#FLAG_SECURE} display flag. The caller promises to take * reasonable measures, such as over-the-air encryption, to prevent the contents * of the display from being intercepted or recorded on a persistent medium. * /pp * Creating a secure virtual display requires the CAPTURE_SECURE_VIDEO_OUTPUT permission. * This permission is reserved for use by system components and is not available to * third-party applications. * /p * * h3Non-secure virtual displays/h3 * p * When this flag is not set, the virtual display is considered unsecure. * The content of secure windows will be blanked if shown on this display. * /p * * see Display#FLAG_SECURE * see #createVirtualDisplay */publicstaticfinalintVIRTUAL_DISPLAY_FLAG_SECURE12;frameworks/base/core/java/android/view/Display.java/** * Display flag: Indicates that the display has a secure video output and * supports compositing secure surfaces. * p * If this flag is set then the display device has a secure video output * and is capable of showing secure surfaces. It may also be capable of * showing {link #FLAG_SUPPORTS_PROTECTED_BUFFERS protected buffers}. * /pp * If this flag is not set then the display device may not have a secure video * output; the user may see a blank region on the screen instead of * the contents of secure surfaces or protected buffers. * /pp * Secure surfaces are used to prevent content rendered into those surfaces * by applications from appearing in screenshots or from being viewed * on non-secure displays. Protected buffers are used by secure video decoders * for a similar purpose. * /pp * An application creates a window with a secure surface by specifying the * {link WindowManager.LayoutParams#FLAG_SECURE} window flag. * Likewise, an application creates a {link SurfaceView} with a secure surface * by calling {link SurfaceView#setSecure} before attaching the secure view to * its containing window. * /pp * An application can use the absence of this flag as a hint that it should not create * secure surfaces or protected buffers on this display because the content may * not be visible. For example, if the flag is not set then the application may * choose not to show content on this display, show an informative error message, * select an alternate content stream or adopt a different strategy for decoding * content that does not rely on secure surfaces or protected buffers. * /p * * see #getFlags */publicstaticfinalintFLAG_SECURE11;VIRTUAL_DISPLAY_FLAG_SECURE这个flag就是用来创建一个安全的虚拟屏幕在创建虚拟屏时候设置了这个标志位后对应的Display对象的flag就会带上FLAG_SECURE带有安全的标志的屏幕就可以展示上面isSecuretrue的Layer但是创建这样一个secure的Display是需要有CAPTURE_SECURE_VIDEO_OUTPUT权限的这个权限对第三方app不可以用。如果没有VIRTUAL_DISPLAY_FLAG_SECURE创建非安全的Display是无法展示那些安全的要求Layer所以一般我们看到现象就是一篇黑。这也就是可以解释支付一些界面无法截图或者截图后是一片黑的现象。Activity设置FLAG_SECURE后普通scrcpy展示情况下面看看scrcpy默认情况下关于scrcpy相关原理内容去看马哥投屏专题展示手势密码设置界面情况如下默认scrcpy的虚拟屏未设置FLAG_SECUREdumpsys display看看相关display情况结果如下可以看出完全scrcpy的虚拟屏幕没有FLAG_SECURE的安全flag所以自然无法展示出有secure为true的Layer。设置FLAG_SECURE具体设置的方法大概就是调用DisplayManager的createVirtualDisplay时候传递flag要或上FLAG_SECURE这个掩码具体在投屏课程也有demo。成功后效果如下对应的dumpsys display看看scrcpy虚拟屏幕情况总结上面有讲到权限虚拟屏幕创建Displayapp创建对于secure的Layer下面总结一下他们关系1、应用通过Secure的flag创建Secure Layer应用将帧数据绘制到Secure Layer。2、SurfaceFlinger进行合成它看到该Secure Layer的目标Display具有FLAG_SECURE。3、创建目标Display具有FLAG_SECURE需要这个app具备CAPTURE_SECURE_VIDEO_OUTPUT权限。