Add CAPTURE_BLACKOUT_CONTENT permission check for screenshots
Previously, only graphics and system were allowed to take screenshots of
layers that are normally blacked out. This change adds an additional check so
processes that have the permission CAPTURE_BLACKOUT_CONTENT can also take
screenshots of that content. Only system applications can have that
permission
Test: Builds, screenshots still work as before
Bug: 173746627
Change-Id: I53a1738be8e4787507a76f8b0ac703f3e413b30c
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 3edbe1d..a1b151b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -287,6 +287,8 @@
const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
const String16 sControlDisplayBrightness("android.permission.CONTROL_DISPLAY_BRIGHTNESS");
const String16 sDump("android.permission.DUMP");
+const String16 sCaptureBlackoutContent("android.permission.CAPTURE_BLACKOUT_CONTENT");
+
const char* KERNEL_IDLE_TIMER_PROP = "graphics.display.kernel_idle_timer.enabled";
// ---------------------------------------------------------------------------
@@ -5681,6 +5683,14 @@
}
}
+static bool hasCaptureBlackoutContentPermission() {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ return uid == AID_GRAPHICS || uid == AID_SYSTEM ||
+ PermissionCache::checkPermission(sCaptureBlackoutContent, pid, uid);
+}
+
static status_t validateScreenshotPermissions(const CaptureArgs& captureArgs) {
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
@@ -5851,6 +5861,10 @@
Rect layerStackSpaceRect;
ui::Dataspace dataspace;
bool captureSecureLayers;
+
+ // Call this before holding mStateLock to avoid any deadlocking.
+ bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();
+
{
Mutex::Autolock lock(mStateLock);
@@ -5860,9 +5874,8 @@
return NAME_NOT_FOUND;
}
- const int uid = IPCThreadState::self()->getCallingUid();
- const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM;
- if (!forSystem && parent->getCurrentState().flags & layer_state_t::eLayerSecure) {
+ if (!canCaptureBlackoutContent &&
+ parent->getCurrentState().flags & layer_state_t::eLayerSecure) {
ALOGW("Attempting to capture secure layer: PERMISSION_DENIED");
return PERMISSION_DENIED;
}
@@ -6012,8 +6025,7 @@
return BAD_VALUE;
}
- const int uid = IPCThreadState::self()->getCallingUid();
- const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM;
+ bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();
static_cast<void>(schedule([=, renderAreaFuture = std::move(renderAreaFuture)]() mutable {
if (mRefreshPending) {
@@ -6033,8 +6045,9 @@
status_t result = NO_ERROR;
renderArea->render([&] {
- result = renderScreenImplLocked(*renderArea, traverseLayers, buffer, forSystem,
- regionSampling, grayscale, captureResults);
+ result = renderScreenImplLocked(*renderArea, traverseLayers, buffer,
+ canCaptureBlackoutContent, regionSampling, grayscale,
+ captureResults);
});
captureResults.result = result;
@@ -6046,8 +6059,9 @@
status_t SurfaceFlinger::renderScreenImplLocked(
const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
- const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool forSystem,
- bool regionSampling, bool grayscale, ScreenCaptureResults& captureResults) {
+ const std::shared_ptr<renderengine::ExternalTexture>& buffer,
+ bool canCaptureBlackoutContent, bool regionSampling, bool grayscale,
+ ScreenCaptureResults& captureResults) {
ATRACE_CALL();
traverseLayers([&](Layer* layer) {
@@ -6060,7 +6074,7 @@
// We allow the system server to take screenshots of secure layers for
// use in situations like the Screen-rotation animation and place
// the impetus on WindowManager to not persist them.
- if (captureResults.capturedSecureLayers && !forSystem) {
+ if (captureResults.capturedSecureLayers && !canCaptureBlackoutContent) {
ALOGW("FB is protected: PERMISSION_DENIED");
return PERMISSION_DENIED;
}