Added captureScreen function for displayId or layerStack
Previously, screencap shell command could accept a display id as an argument.
It would use the id to look up information about the display (displayToken,
orientation, colorMode) by making requests into SurfaceFlinger. Since those
requests only worked for physical display ids, screencap didn't work to capture
virtual displays.
Instead of adding new methods to look up display information for a virtual
display, this change just adds a new captureScreen function in SurfaceFlinger
that accepts a displayId or layerStack and handles getting the default info
for the requested display. This works for both physical and virtual displays.
Test: adb shell screencap -d <layerStack for virt display>
Fixes: 130974213
Change-Id: I24b7558c973a057414c6b4f81ab1d60152fff38d
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index fc0d5fc..8be24b5 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -5086,6 +5086,14 @@
ALOGE("Attempting to access SurfaceFlinger with unused code: %u", code);
return PERMISSION_DENIED;
}
+ case CAPTURE_SCREEN_BY_ID: {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int uid = ipc->getCallingUid();
+ if ((uid == AID_GRAPHICS) || (uid == AID_SYSTEM) || (uid == AID_SHELL)) {
+ return OK;
+ }
+ return PERMISSION_DENIED;
+ }
}
// These codes are used for the IBinder protocol to either interrogate the recipient
@@ -5500,6 +5508,66 @@
useIdentityTransform, outCapturedSecureLayers);
}
+static Dataspace pickDataspaceFromColorMode(const ColorMode colorMode) {
+ switch (colorMode) {
+ case ColorMode::DISPLAY_P3:
+ case ColorMode::BT2100_PQ:
+ case ColorMode::BT2100_HLG:
+ case ColorMode::DISPLAY_BT2020:
+ return Dataspace::DISPLAY_P3;
+ default:
+ return Dataspace::V0_SRGB;
+ }
+}
+
+const sp<DisplayDevice> SurfaceFlinger::getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) {
+ const sp<IBinder> displayToken = getPhysicalDisplayTokenLocked(DisplayId{displayOrLayerStack});
+ if (displayToken) {
+ return getDisplayDeviceLocked(displayToken);
+ }
+ // Couldn't find display by displayId. Try to get display by layerStack since virtual displays
+ // may not have a displayId.
+ for (const auto& [token, display] : mDisplays) {
+ if (display->getLayerStack() == displayOrLayerStack) {
+ return display;
+ }
+ }
+ return nullptr;
+}
+
+status_t SurfaceFlinger::captureScreen(uint64_t displayOrLayerStack, Dataspace* outDataspace,
+ sp<GraphicBuffer>* outBuffer) {
+ sp<DisplayDevice> display;
+ uint32_t width;
+ uint32_t height;
+ ui::Transform::orientation_flags captureOrientation;
+ {
+ Mutex::Autolock _l(mStateLock);
+ display = getDisplayByIdOrLayerStack(displayOrLayerStack);
+ if (!display) {
+ return BAD_VALUE;
+ }
+
+ width = uint32_t(display->getViewport().width());
+ height = uint32_t(display->getViewport().height());
+
+ captureOrientation = fromSurfaceComposerRotation(
+ static_cast<ISurfaceComposer::Rotation>(display->getOrientation()));
+ *outDataspace =
+ pickDataspaceFromColorMode(display->getCompositionDisplay()->getState().colorMode);
+ }
+
+ DisplayRenderArea renderArea(display, Rect(), width, height, *outDataspace, captureOrientation,
+ false /* captureSecureLayers */);
+
+ auto traverseLayers = std::bind(&SurfaceFlinger::traverseLayersInDisplay, this, display,
+ std::placeholders::_1);
+ bool ignored = false;
+ return captureScreenCommon(renderArea, traverseLayers, outBuffer, ui::PixelFormat::RGBA_8888,
+ false /* useIdentityTransform */,
+ ignored /* outCapturedSecureLayers */);
+}
+
status_t SurfaceFlinger::captureLayers(
const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer,
const Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,