
背景在马哥发布关于豆包手机安全危险权限文章聊一聊豆包AI手机助手高度敏感权限CAPTURE_SECURE_VIDEO_OUTPUT后一天豆包手机助手官方也有一个声明文章当然本文先不对这些敏感权限危险程度做啥解释只是作为一个fw系统开发者技术角度深入了解系统剖析这些权限的使用场景和作用。下面来看看权限READ_FRAME_BUFFER。系统中使用READ_FRAME_BUFFER搜索系统中对这个权限校验地方如下上面可以看出主要是针对截图截屏等方法的需要该权限的判断熟悉点的方法比如screenshotWallpapercaptureDisplay都是截图相关的api接口。captureDisplay为案例进行分析frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.javaOverridepublicvoidcaptureDisplay(intdisplayId,Nullable ScreenCapture.CaptureArgs captureArgs,ScreenCapture.ScreenCaptureListener listener){Slog.d(TAG,captureDisplay);//检验READ_FRAME_BUFFER权限是否有if(!checkCallingPermission(READ_FRAME_BUFFER,captureDisplay())){thrownewSecurityException(Requires READ_FRAME_BUFFER permission);}//权限校验通过则ScreenCapture.LayerCaptureArgs layerCaptureArgsgetCaptureArgs(displayId,captureArgs);ScreenCapture.captureLayers(layerCaptureArgs,listener);if(Binder.getCallingUid()!SYSTEM_UID){// Release the SurfaceControl objects only if the caller is not in system server as no// parcelling occurs in this case.layerCaptureArgs.release();}}接下来看看captureLayers这部分的源码。frameworks/base/core/java/android/window/ScreenCapture.java/** * param captureArgs Arguments about how to take the screenshot * param captureListener A listener to receive the screenshot callback * hide */publicstaticintcaptureLayers(NonNull LayerCaptureArgs captureArgs,NonNull ScreenCaptureListener captureListener){returnnativeCaptureLayers(captureArgs,captureListener.mNativeObject,false/* sync */);}captureArgs封装捕获参数核心包括 displayId目标显示屏 ID、待捕获的图层列表、捕获分辨率 / 格式其实这个参数是核心中核心。captureListener异步回调接收捕获结果成功返回 ScreenCaptureResult包含图层帧数据失败返回错误码。frameworks/base/core/jni/android_window_ScreenCapture.cppstaticjintnativeCaptureLayers(JNIEnv*env,jclass clazz,jobject layerCaptureArgsObject,jlong screenCaptureListenerObject,jboolean sync){LayerCaptureArgs layerCaptureArgs;getCaptureArgs(env,layerCaptureArgsObject,layerCaptureArgs.captureArgs);SurfaceControl*layerreinterpret_castSurfaceControl*(env-GetLongField(layerCaptureArgsObject,gLayerCaptureArgsClassInfo.layer));if(layernullptr){returnBAD_VALUE;}//参数转换layerCaptureArgs.layerHandlelayer-getHandle();layerCaptureArgs.childrenOnlyenv-GetBooleanField(layerCaptureArgsObject,gLayerCaptureArgsClassInfo.childrenOnly);spgui::IScreenCaptureListenercaptureListenerreinterpret_castgui::IScreenCaptureListener*(screenCaptureListenerObject);//调用到 ScreenshotClient::captureLayersreturnScreenshotClient::captureLayers(layerCaptureArgs,captureListener,sync);}下面看看ScreenshotClient::captureLayers方法frameworks/native/libs/gui/SurfaceComposerClient.cppstatus_tScreenshotClient::captureLayers(constLayerCaptureArgscaptureArgs,constspIScreenCaptureListenercaptureListener,boolsync){spgui::ISurfaceComposers(ComposerServiceAIDL::getComposerService());if(snullptr)returnNO_INIT;binder::Status status;if(sync){gui::ScreenCaptureResults captureResults;//这里会调用到sf层面statuss-captureLayersSync(captureArgs,captureResults);captureListener-onScreenCaptureCompleted(captureResults);}else{statuss-captureLayers(captureArgs,captureListener);}returnstatusTFromBinderStatus(status);}SurfaceFlinger部分的captureLayers方法voidSurfaceFlinger::captureLayers(constLayerCaptureArgsargs,constspIScreenCaptureListenercaptureListener){SFTRACE_CALL();constautocaptureArgsargs.captureArgs;//这里会再次校验权限READ_FRAME_BUFFERstatus_t validatevalidateScreenshotPermissions(captureArgs);if(validate!OK){ALOGD(Permission denied to captureLayers);invokeScreenCaptureError(validate,captureListener);return;}//省略ScreenshotArgs screenshotArgs;screenshotArgs.captureTypeVariantparent-getSequence();screenshotArgs.childrenOnlyargs.childrenOnly;screenshotArgs.sourceCropcrop;screenshotArgs.reqSizereqSize;screenshotArgs.dataspacestatic_castui::Dataspace(captureArgs.dataspace);screenshotArgs.isSecurecaptureArgs.captureSecureLayers;screenshotArgs.seamlessTransitioncaptureArgs.hintForSeamlessTransition;//准备参数调用到captureScreenCommon方法captureScreenCommon(screenshotArgs,getLayerSnapshotsFn,reqSize,static_castui::PixelFormat(captureArgs.pixelFormat),captureArgs.allowProtected,captureArgs.grayscale,captureListener);}这里主要就是先进行权限检测再调用到captureScreenCommon方法。先看看validateScreenshotPermissions校验方法staticstatus_tvalidateScreenshotPermissions(constCaptureArgscaptureArgs){IPCThreadState*ipcIPCThreadState::self();constintpidipc-getCallingPid();constintuidipc-getCallingUid();//检测ReadFrameBuffer权限if(uidAID_GRAPHICS||uidAID_SYSTEM||PermissionCache::checkPermission(sReadFramebuffer,pid,uid)){returnOK;}ALOGE(Permission Denial: cant take screenshot pid%d, uid%d,pid,uid);returnPERMISSION_DENIED;}再看看captureScreenCommon方法voidSurfaceFlinger::captureScreenCommon(ScreenshotArgsargs,GetLayerSnapshotsFunction getLayerSnapshotsFn,ui::Size bufferSize,ui::PixelFormat reqPixelFormat,boolallowProtected,boolgrayscale,constspIScreenCaptureListenercaptureListener){//省略//这里会调用到captureScreenshot方法而且返回fence进行等待autofutureFencecaptureScreenshot(args,texture,false/* regionSampling */,grayscale,isProtected,captureListener,layers,hdrTexture,gainmapTexture);futureFence.get();}那么看看captureScreenshot方法ftl::SharedFutureFenceResultSurfaceFlinger::captureScreenshot(ScreenshotArgsargs,conststd::shared_ptrrenderengine::ExternalTexturebuffer,boolregionSampling,boolgrayscale,boolisProtected,constspIScreenCaptureListenercaptureListener,conststd::vectorstd::pairLayer*,spLayerFElayers,conststd::shared_ptrrenderengine::ExternalTexturehdrBuffer,conststd::shared_ptrrenderengine::ExternalTexturegainmapBuffer){SFTRACE_CALL();ScreenCaptureResults captureResults;ftl::SharedFutureFenceResultrenderFuture;floathdrSdrRatioargs.displayBrightnessNits/args.sdrWhitePointNits;if(hdrBuffergainmapBuffer){//省略}else{//这里主要又调用到renderScreenImplrenderFuturerenderScreenImpl(args,buffer,regionSampling,grayscale,isProtected,captureResults,layers);}if(captureListener){// Defer blocking on renderFuture back to the Binder thread.//进行回调到captureListenerreturnftl::Future(std::move(renderFuture)).then([captureListener,captureResultsstd::move(captureResults),hdrSdrRatio](FenceResult fenceResult)mutable-FenceResult{captureResults.fenceResultstd::move(fenceResult);captureResults.hdrSdrRatiohdrSdrRatio;captureListener-onScreenCaptureCompleted(captureResults);returnbase::unexpected(NO_ERROR);}).share();}returnrenderFuture;}那么看看renderScreenImpl方法ftl::SharedFutureFenceResultSurfaceFlinger::renderScreenImpl(ScreenshotArgsargs,conststd::shared_ptrrenderengine::ExternalTexturebuffer,boolregionSampling,boolgrayscale,boolisProtected,ScreenCaptureResultscaptureResults,conststd::vectorstd::pairLayer*,spLayerFElayers){//省略部分//创建渲染layer到截图的表达式presentautopresent[this,buffercapturedBuffer,dataspacecaptureResults.capturedDataspace,grayscale,isProtected,layers,layerStack,regionSampling,args,renderIntent,enableLocalTonemapping]()-FenceResult{std::unique_ptrcompositionengine::CompositionEnginecompositionEnginemFactory.createCompositionEngine();compositionEngine-setRenderEngine(mRenderEngine.get());compositionEngine-setHwComposer(mHWComposer.get());std::vectorspcompositionengine::LayerFElayerFEs;layerFEs.reserve(layers.size());for(auto[layer,layerFE]:layers){// Release fences were not yet added for non-threaded render engine. To avoid// deadlocks between main thread and binder threads waiting for the future fence// result, fences should be added to layers in the same hop onto the main thread.if(!mRenderEngine-isThreaded()){attachReleaseFenceFutureToLayer(layer,layerFE.get(),ui::INVALID_LAYER_STACK);}layerFEs.push_back(layerFE);}compositionengine::Output::ColorProfile colorProfile{.dataspacedataspace,.renderIntentrenderIntent};floattargetBrightness1.0f;if(enableLocalTonemapping){// Boost the whole scene so that SDR white is at 1.0 while still communicating the hdr// sdr ratio via display brightness / sdrWhite nits.targetBrightnessargs.sdrWhitePointNits/args.displayBrightnessNits;}elseif(dataspaceui::Dataspace::BT2020_HLG){constfloatmaxBrightnessNitsargs.displayBrightnessNits/args.sdrWhitePointNits*203;// With a low dimming ratio, dont fit the entire curve. Otherwise mixed content// will appear way too bright.if(maxBrightnessNits1000.f){targetBrightness1000.f/maxBrightnessNits;}}// Capturing screenshots using layers have a clear capture fill (0 alpha).// Capturing via display or displayId, which do not use args.layerSequence,// has an opaque capture fill (1 alpha).constfloatlayerAlphastd::holds_alternativeint32_t(args.captureTypeVariant)?0.0f:1.0f;// Screenshots leaving the device must not dim in gamma space.constbooldimInGammaSpaceForEnhancedScreenshotsmDimInGammaSpaceForEnhancedScreenshotsargs.seamlessTransition;std::shared_ptrScreenCaptureOutputoutputcreateScreenCaptureOutput(ScreenCaptureOutputArgs{.compositionEngine*compositionEngine,.colorProfilecolorProfile,.layerStacklayerStack,.sourceCropargs.sourceCrop,.bufferstd::move(buffer),.displayIdVariantargs.displayIdVariant,.reqBufferSizeargs.reqSize,.sdrWhitePointNitsargs.sdrWhitePointNits,.displayBrightnessNitsargs.displayBrightnessNits,.targetBrightnesstargetBrightness,.layerAlphalayerAlpha,.regionSamplingregionSampling,.treat170mAsSrgbmTreat170mAsSrgb,.dimInGammaSpaceForEnhancedScreenshotsdimInGammaSpaceForEnhancedScreenshots,.isSecureargs.isSecure,.isProtectedisProtected,.enableLocalTonemappingenableLocalTonemapping});constfloatcolorSaturationgrayscale?0:1;//准备好CompositionRefreshArgs参数compositionengine::CompositionRefreshArgs refreshArgs{.outputs{output},.layersstd::move(layerFEs),.updatingOutputGeometryThisFrametrue,.updatingGeometryThisFrametrue,.colorTransformMatrixcalculateColorMatrix(colorSaturation),};//调用present进行合成compositionEngine-present(refreshArgs);returnoutput-getRenderSurface()-getClientTargetAcquireFence();};// If RenderEngine is threaded, we can safely call CompositionEngine::present off the main// thread as the RenderEngine::drawLayers call will run on RenderEngines thread. Otherwise,// we need RenderEngine to run on the main thread so we call CompositionEngine::present// immediately.//// TODO(b/196334700) Once we use RenderEngineThreaded everywhere we can always defer the call// to CompositionEngine::present.ftl::SharedFutureFenceResultpresentFuturemRenderEngine-isThreaded()?ftl::yield(present()).share():mScheduler-schedule(std::move(present)).share();returnpresentFuture;}这个方法比较多这个方法本身也在马哥SurfaceFlinger课程中有讲解分析过大概干了以下几个事情收集待渲染图层的元信息Secure/HDR 标记适配色彩 / 亮度参数保证显示一致性通过 CompositionEngine 将图层合成到目标缓冲区基于线程模型调度渲染逻辑返回Fence。本文主要带大家来剖析一下READ_FRAME_BUFFER权限的使用场景这块其实整体上和豆包官方助手说的基本上没啥大差别。READ_FRAME_BUFFER是安卓系统级权限需要系统签名普通第三方app无法获取核心功能是调用sf接口sf把对应layer进行gpu绘制到截图buffer上完全不是网络说的没有任何限制的直接读取gpu内容。但是最后留下个疑问思考READ_FRAME_BUFFER真的只能截图普通图层就不可以截取secure的窗口图层吗原文地址https://mp.weixin.qq.com/s/QZuZrQkLgbIouz9BrVZ66Q更多framework是做开发干货请关注下面“千里马学框架”