Allow screen capture for a specified uid.
Currently, screenshots can either be done on the entire display or for a
layer hierarchy. Screenshots can only be taken by a system process or a
process with the correct permission. This change introduces a way for an
app to screenshot itself without introducing security issues
The following checks are done:
1. If the request is from graphics or has READ_FRAME_BUFFER permission
flag, the request is allowed. If a uid was set to non -1, only layers
from the specified uid will be captured. If uid in args is -1,
everything will be in the screenshot, so no layers will be skipped
2. If the request doesn't have the permissions, the calling uid is
compared to the uid in the request. If they match, we allow the
screenshot code to proceed and only screenshot the layers with the
matching uid.
3. Otherwise, we return with PERMISSION_DENIED
Test: ScreenCaptureTest
Bug: 155825630
Change-Id: I129b5a7f489383bf6d2f0ee333a416babc1444a2
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 9d9a4cf..6fae56a 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -4958,11 +4958,13 @@
// special permissions.
case SET_FRAME_RATE:
case GET_DISPLAY_BRIGHTNESS_SUPPORT:
+ // captureLayers and captureDisplay will handle the permission check in the function
+ case CAPTURE_LAYERS:
+ case CAPTURE_DISPLAY:
case SET_DISPLAY_BRIGHTNESS: {
return OK;
}
- case CAPTURE_LAYERS:
- case CAPTURE_DISPLAY:
+
case ADD_REGION_SAMPLING_LISTENER:
case REMOVE_REGION_SAMPLING_LISTENER: {
// codes that require permission check
@@ -5434,10 +5436,33 @@
}
}
+static status_t validateScreenshotPermissions(const CaptureArgs& captureArgs) {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ if (uid == AID_GRAPHICS || PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) {
+ return OK;
+ }
+
+ // If the caller doesn't have the correct permissions but is only attempting to screenshot
+ // itself, we allow it to continue.
+ if (captureArgs.uid == uid) {
+ return OK;
+ }
+
+ ALOGE("Permission Denial: can't take screenshot pid=%d, uid=%d", pid, uid);
+ return PERMISSION_DENIED;
+}
+
status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args,
ScreenCaptureResults& captureResults) {
ATRACE_CALL();
+ status_t validate = validateScreenshotPermissions(args);
+ if (validate != OK) {
+ return validate;
+ }
+
if (!args.displayToken) return BAD_VALUE;
auto renderAreaRotation = ui::Transform::toRotationFlags(args.rotation);
@@ -5472,8 +5497,8 @@
renderAreaRotation, args.captureSecureLayers);
});
- auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) {
- traverseLayersInLayerStack(layerStack, visitor);
+ auto traverseLayers = [this, args, layerStack](const LayerVector::Visitor& visitor) {
+ traverseLayersInLayerStack(layerStack, args.uid, visitor);
};
return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
args.pixelFormat, args.useIdentityTransform, captureResults);
@@ -5567,7 +5592,7 @@
});
auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) {
- traverseLayersInLayerStack(layerStack, visitor);
+ traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, visitor);
};
return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, size,
@@ -5579,6 +5604,11 @@
ScreenCaptureResults& captureResults) {
ATRACE_CALL();
+ status_t validate = validateScreenshotPermissions(args);
+ if (validate != OK) {
+ return validate;
+ }
+
ui::Size reqSize;
sp<Layer> parent;
Rect crop(args.sourceCrop);
@@ -5652,19 +5682,19 @@
}
bool childrenOnly = args.childrenOnly;
-
RenderAreaFuture renderAreaFuture = promise::defer([=]() -> std::unique_ptr<RenderArea> {
return std::make_unique<LayerRenderArea>(*this, parent, crop, reqSize, dataspace,
childrenOnly, displayViewport,
captureSecureLayers);
});
- auto traverseLayers = [parent, childrenOnly,
- &excludeLayers](const LayerVector::Visitor& visitor) {
+ auto traverseLayers = [parent, args, &excludeLayers](const LayerVector::Visitor& visitor) {
parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
if (!layer->isVisible()) {
return;
- } else if (childrenOnly && layer == parent.get()) {
+ } else if (args.childrenOnly && layer == parent.get()) {
+ return;
+ } else if (args.uid != CaptureArgs::UNSET_UID && args.uid != layer->getOwnerUid()) {
return;
}
@@ -5890,22 +5920,25 @@
layersSortedByZ.traverseInReverseZOrder(stateSet, visitor);
}
-void SurfaceFlinger::traverseLayersInLayerStack(ui::LayerStack layerStack,
+void SurfaceFlinger::traverseLayersInLayerStack(ui::LayerStack layerStack, const int32_t uid,
const LayerVector::Visitor& visitor) {
// We loop through the first level of layers without traversing,
// as we need to determine which layers belong to the requested display.
for (const auto& layer : mDrawingState.layersSortedByZ) {
- if (!layer->belongsToDisplay(layerStack, false)) {
+ if (!layer->belongsToDisplay(layerStack)) {
continue;
}
// relative layers are traversed in Layer::traverseInZOrder
layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
- if (!layer->belongsToDisplay(layerStack, false)) {
+ if (layer->getPrimaryDisplayOnly()) {
return;
}
if (!layer->isVisible()) {
return;
}
+ if (uid != CaptureArgs::UNSET_UID && layer->getOwnerUid() != uid) {
+ return;
+ }
visitor(layer);
});
}