Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c62572fa authored by chaviw's avatar chaviw Committed by Chavi Weingarten
Browse files

Allow offscreen mirrored layers to be captured.

If an offscreen layer is valid and has content, we should be able to
take a screen capture of it.

If we're mirroring content, but it's not on screen, we need
to ensure updateMirrorInfo is called to get the updated hierarchy. This
will allow the mirror content to get properly screenshot

Test: screencapture offscreen
Test: MirrorLayerTest
Test: ScreenCaptureTest
Change-Id: I31f806eb616e2d6f800da6328c9878a3e47d6a14
Merged-In: I31f806eb616e2d6f800da6328c9878a3e47d6a14
Bug: 188222480
parent e6adce59
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -94,8 +94,22 @@ void LayerRenderArea::render(std::function<void()> drawLayers) {
    // no need to check rotation because there is none
    mNeedsFiltering = sourceCrop.width() != getReqWidth() || sourceCrop.height() != getReqHeight();

    // If layer is offscreen, update mirroring info if it exists
    if (mLayer->isRemovedFromCurrentState()) {
        mLayer->traverse(LayerVector::StateSet::Drawing,
                         [&](Layer* layer) { layer->updateMirrorInfo(); });
        mLayer->traverse(LayerVector::StateSet::Drawing,
                         [&](Layer* layer) { layer->updateCloneBufferInfo(); });
    }

    if (!mChildrenOnly) {
        mTransform = mLayer->getTransform().inverse();
        // If the layer is offscreen, compute bounds since we don't compute bounds for offscreen
        // layers in a regular cycles.
        if (mLayer->isRemovedFromCurrentState()) {
            FloatRect maxBounds = mFlinger.getMaxDisplayBounds();
            mLayer->computeBounds(maxBounds, ui::Transform(), 0.f /* shadowRadius */);
        }
        drawLayers();
    } else {
        uint32_t w = static_cast<uint32_t>(getWidth());
+20 −20
Original line number Diff line number Diff line
@@ -2455,7 +2455,7 @@ void SurfaceFlinger::postComposition() {
    }
}

void SurfaceFlinger::computeLayerBounds() {
FloatRect SurfaceFlinger::getMaxDisplayBounds() {
    // Find the largest width and height among all the displays.
    int32_t maxDisplayWidth = 0;
    int32_t maxDisplayHeight = 0;
@@ -2473,8 +2473,13 @@ void SurfaceFlinger::computeLayerBounds() {

    // Ignore display bounds for now since they will be computed later. Use a large Rect bound
    // to ensure it's bigger than an actual display will be.
    FloatRect maxBounds(-maxDisplayWidth * 10, -maxDisplayHeight * 10, maxDisplayWidth * 10,
                        maxDisplayHeight * 10);
    FloatRect maxBounds = FloatRect(-maxDisplayWidth * 10, -maxDisplayHeight * 10,
                                    maxDisplayWidth * 10, maxDisplayHeight * 10);
    return maxBounds;
}

void SurfaceFlinger::computeLayerBounds() {
    FloatRect maxBounds = getMaxDisplayBounds();
    for (const auto& layer : mDrawingState.layersSortedByZ) {
        layer->computeBounds(maxBounds, ui::Transform(), 0.f /* shadowRadius */);
    }
@@ -6167,9 +6172,7 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
    sp<Layer> parent;
    Rect crop(args.sourceCrop);
    std::unordered_set<sp<Layer>, ISurfaceComposer::SpHash<Layer>> excludeLayers;
    Rect layerStackSpaceRect;
    ui::Dataspace dataspace;
    bool captureSecureLayers;

    // Call this before holding mStateLock to avoid any deadlocking.
    bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();
@@ -6178,7 +6181,7 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
        Mutex::Autolock lock(mStateLock);

        parent = fromHandle(args.layerHandle).promote();
        if (parent == nullptr || parent->isRemovedFromCurrentState()) {
        if (parent == nullptr) {
            ALOGE("captureLayers called with an invalid or removed parent");
            return NAME_NOT_FOUND;
        }
@@ -6217,39 +6220,36 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
            }
        }

        const auto display = findDisplay(WithLayerStack(parent->getLayerStack()));
        if (!display) {
            return NAME_NOT_FOUND;
        }

        layerStackSpaceRect = display->getLayerStackSpaceRect();

        // The dataspace is depended on the color mode of display, that could use non-native mode
        // (ex. displayP3) to enhance the content, but some cases are checking native RGB in bytes,
        // and failed if display is not in native mode. This provide a way to force using native
        // colors when capture.
        dataspace = args.dataspace;
        if (dataspace == ui::Dataspace::UNKNOWN) {
            auto display = findDisplay(WithLayerStack(parent->getLayerStack()));
            if (!display) {
                // If the layer is not on a display, use the dataspace for the default display.
                display = getDefaultDisplayDeviceLocked();
            }

            const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode;
            dataspace = pickDataspaceFromColorMode(colorMode);
        }

        captureSecureLayers = args.captureSecureLayers && display->isSecure();
    } // mStateLock

    // really small crop or frameScale
    if (reqSize.width <= 0) {
        reqSize.width = 1;
    }
    if (reqSize.height <= 0) {
        reqSize.height = 1;
    if (reqSize.width <= 0 || reqSize.height <= 0) {
        ALOGW("Failed to captureLayes: crop or scale too small");
        return BAD_VALUE;
    }

    Rect layerStackSpaceRect(0, 0, reqSize.width, reqSize.height);
    bool childrenOnly = args.childrenOnly;
    RenderAreaFuture renderAreaFuture = ftl::defer([=]() -> std::unique_ptr<RenderArea> {
        return std::make_unique<LayerRenderArea>(*this, parent, crop, reqSize, dataspace,
                                                 childrenOnly, layerStackSpaceRect,
                                                 captureSecureLayers);
                                                 args.captureSecureLayers);
    });

    auto traverseLayers = [parent, args, excludeLayers](const LayerVector::Visitor& visitor) {
+1 −0
Original line number Diff line number Diff line
@@ -337,6 +337,7 @@ public:
    // Disables expensive rendering for all displays
    // This is scheduled on the main thread
    void disableExpensiveRendering();
    FloatRect getMaxDisplayBounds();

protected:
    // We're reference counted, never destroy SurfaceFlinger directly
+0 −11
Original line number Diff line number Diff line
@@ -52,17 +52,6 @@ protected:
    }
};

TEST_F(InvalidHandleTest, createSurfaceInvalidParentHandle) {
    // The createSurface is scheduled now, we could still get a created surface from createSurface.
    // Should verify if it actually added into current state by checking the screenshot.
    auto notSc = mScc->createSurface(String8("lolcats"), 19, 47, PIXEL_FORMAT_RGBA_8888, 0,
                                     mNotSc->getHandle());
    LayerCaptureArgs args;
    args.layerHandle = notSc->getHandle();
    ScreenCaptureResults captureResults;
    ASSERT_EQ(NAME_NOT_FOUND, ScreenCapture::captureLayers(args, captureResults));
}

TEST_F(InvalidHandleTest, captureLayersInvalidHandle) {
    LayerCaptureArgs args;
    args.layerHandle = mNotSc->getHandle();
+55 −0
Original line number Diff line number Diff line
@@ -273,6 +273,61 @@ TEST_F(MirrorLayerTest, InitialMirrorState) {
    }
}

// Test that a mirror layer can be screenshot when offscreen
TEST_F(MirrorLayerTest, OffscreenMirrorScreenshot) {
    const auto display = SurfaceComposerClient::getInternalDisplayToken();
    ui::DisplayMode mode;
    SurfaceComposerClient::getActiveDisplayMode(display, &mode);
    const ui::Size& size = mode.resolution;

    sp<SurfaceControl> grandchild =
            createLayer("Grandchild layer", 50, 50, ISurfaceComposerClient::eFXSurfaceBufferState,
                        mChildLayer.get());
    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(grandchild, Color::BLUE, 50, 50));
    Rect childBounds = Rect(50, 50, 450, 450);

    asTransaction([&](Transaction& t) {
        t.setCrop(grandchild, Rect(0, 0, 50, 50)).show(grandchild);
        t.setFlags(grandchild, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
    });

    sp<SurfaceControl> mirrorLayer = nullptr;
    {
        // Run as system to get the ACCESS_SURFACE_FLINGER permission when mirroring
        UIDFaker f(AID_SYSTEM);
        // Mirror mChildLayer
        mirrorLayer = mClient->mirrorSurface(mChildLayer.get());
        ASSERT_NE(mirrorLayer, nullptr);
    }

    // Show the mirror layer, but don't reparent to a layer on screen.
    Transaction().show(mirrorLayer).apply();

    {
        SCOPED_TRACE("Offscreen Mirror");
        auto shot = screenshot();
        shot->expectColor(Rect(0, 0, size.getWidth(), 50), Color::RED);
        shot->expectColor(Rect(0, 0, 50, size.getHeight()), Color::RED);
        shot->expectColor(Rect(450, 0, size.getWidth(), size.getHeight()), Color::RED);
        shot->expectColor(Rect(0, 450, size.getWidth(), size.getHeight()), Color::RED);
        shot->expectColor(Rect(100, 100, 450, 450), Color::GREEN);
        shot->expectColor(Rect(50, 50, 100, 100), Color::BLUE);
    }

    {
        SCOPED_TRACE("Capture Mirror");
        // Capture just the mirror layer and child.
        LayerCaptureArgs captureArgs;
        captureArgs.layerHandle = mirrorLayer->getHandle();
        captureArgs.sourceCrop = childBounds;
        std::unique_ptr<ScreenCapture> shot;
        ScreenCapture::captureLayers(&shot, captureArgs);
        shot->expectSize(childBounds.width(), childBounds.height());
        shot->expectColor(Rect(0, 0, 50, 50), Color::BLUE);
        shot->expectColor(Rect(50, 50, 400, 400), Color::GREEN);
    }
}

} // namespace android

// TODO(b/129481165): remove the #pragma below and fix conversion issues
Loading