Android11 截图过滤特定图层
思路
从可执行文件screencap源码分析,发现会执行如下方法
继续追踪ScreenshotClient类
ScreenshotClient
可以看到最终是走向了SurfaceFlinger的captureScreen方法。
captureScreen
SurfaceFlinger的captureScreen代码如下,走到了captureScreenCommon方法
captureScreenCommon
继续往下跟踪是重载函数,流程依次是
captureScreenCommon(renderArea, traverseLayers, outBuffer, ui::PixelFormat::RGBA_8888,
false /* useIdentityTransform */,ignored /* outCapturedSecureLayers */);
captureScreenCommon(renderArea, traverseLayers, *outBuffer, useIdentityTransform,
false /* regionSampling */, outCapturedSecureLayers);
captureScreenImplLocked(renderArea, traverseLayers, buffer.get(),
useIdentityTransform, forSystem, &fd,
regionSampling, outCapturedSecureLayers);
captureScreenImplLocked
captureScreenImplLocked方法代码如下,调用了renderScreenImplLocked方法
对策一
## SurfaceFlinger.cpp
void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea,
TraverseLayersFunction traverseLayers,
ANativeWindowBuffer* buffer, bool useIdentityTransform,
bool regionSampling, int* outSyncFd) {
.....
traverseLayers([&](Layer* layer) {
const bool supportProtectedContent = false;
Region clip(renderArea.getBounds());
compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{
clip,
useIdentityTransform,
layer->needsFilteringForScreenshots(display.get(), transform) ||
renderArea.needsFiltering(),
renderArea.isSecure(),
supportProtectedContent,
clearRegion,
displayViewport,
clientCompositionDisplay.outputDataspace,
true, /* realContentIsVisible */
false, /* clearContent */
};
std::vector<compositionengine::LayerFE::LayerSettings> results =
layer->prepareClientCompositionList(targetSettings);
if (results.size() > 0) {
for (auto& settings : results) {
settings.geometry.positionTransform =
transform.asMatrix4() * settings.geometry.positionTransform;
// There's no need to process blurs when we're executing region sampling,
// we're just trying to understand what we're drawing, and doing so without
// blurs is already a pretty good approximation.
if (regionSampling) {
settings.backgroundBlurRadius = 0;
}
}
//add start
std::string layerName = layer->getName();
ALOGE("Test layerName= %s ",layerName.c_str());
if( strstr(layerName.c_str(), "Sprite") == NULL &&
strstr(layerName.c_str(), "com.skg.benqsetting") == NULL){
clientCompositionLayers.insert(clientCompositionLayers.end(),
std::make_move_iterator(results.begin()),
std::make_move_iterator(results.end()));
ALOGE("Test push_back() ");
renderedLayers.push_back(layer);
}
//add end
}
});
...
}
对策二
查看代码的时候发现,layer有一个属性mPrimaryDisplayOnly,通过设置这个变量可以实现截屏不包括当前layer,系统代码注释如下
##SurfaceFlinger.cpp
status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& client, uint32_t w,
uint32_t h, PixelFormat format, uint32_t flags,
LayerMetadata metadata, sp<IBinder>* handle,
sp<IGraphicBufferProducer>* gbp,
const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer,
uint32_t* outTransformHint) {
if (int32_t(w|h) < 0) {
ALOGE("createLayer() failed, w or h is negative (w=%d, h=%d)",
int(w), int(h));
return BAD_VALUE;
}
ALOG_ASSERT(parentLayer == nullptr || parentHandle == nullptr,
"Expected only one of parentLayer or parentHandle to be non-null. "
"Programmer error?");
status_t result = NO_ERROR;
sp<Layer> layer;
std::string uniqueName = getUniqueLayerName(name.string());
bool primaryDisplayOnly = false;
// window type is WINDOW_TYPE_DONT_SCREENSHOT from SurfaceControl.java
// TODO b/64227542
if (metadata.has(METADATA_WINDOW_TYPE)) {
int32_t windowType = metadata.getInt32(METADATA_WINDOW_TYPE, 0);
if (windowType == 441731) {
metadata.setInt32(METADATA_WINDOW_TYPE, InputWindowInfo::TYPE_NAVIGATION_BAR_PANEL);
primaryDisplayOnly = true;
}
}
....
if (primaryDisplayOnly) {
layer->setPrimaryDisplayOnly();
}
}
可以看到当windowType == 441731的时候,primaryDisplayOnly被设置为true,继续跟踪441731,注释上已经说明441731是SurfaceControl 中的 WINDOW_TYPE_DONT_SCREENSHOT
继续跟踪WINDOW_TYPE_DONT_SCREENSHOT
可以看到,当窗口privateFlags包含PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY时,windowType会设置为WINDOW_TYPE_DONT_SCREENSHOT,而createSurfaceLocked是在wms添加窗口时调用的
WindowManager.LayoutParams params = getWindow().getAttributes();
params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
getWindow().setAttributes(params);
添加对策如下,尝试之后截图发现layer未出现。当privateFlags只有系统应用能调用到,Android Studio SDK不支持该属性更改。