| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| // TODO(b/129481165): remove the #pragma below and fix conversion issues |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wconversion" |
| |
| #include <android-base/properties.h> |
| #include <private/android_filesystem_config.h> |
| #include "LayerTransactionTest.h" |
| #include "utils/TransactionUtils.h" |
| |
| namespace android { |
| |
| class MirrorLayerTest : public LayerTransactionTest { |
| protected: |
| virtual void SetUp() { |
| LayerTransactionTest::SetUp(); |
| ASSERT_EQ(NO_ERROR, mClient->initCheck()); |
| const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); |
| ASSERT_FALSE(ids.empty()); |
| |
| const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); |
| ASSERT_FALSE(display == nullptr); |
| |
| mParentLayer = createColorLayer("Parent layer", Color::RED); |
| mChildLayer = createColorLayer("Child layer", Color::GREEN, mParentLayer.get()); |
| asTransaction([&](Transaction& t) { |
| t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK); |
| t.setLayer(mParentLayer, INT32_MAX - 2).show(mParentLayer); |
| t.setCrop(mChildLayer, Rect(0, 0, 400, 400)).show(mChildLayer); |
| t.setPosition(mChildLayer, 50, 50); |
| t.setFlags(mParentLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque); |
| t.setFlags(mChildLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque); |
| }); |
| } |
| |
| virtual void TearDown() { |
| LayerTransactionTest::TearDown(); |
| mParentLayer = 0; |
| mChildLayer = 0; |
| } |
| |
| sp<SurfaceControl> mParentLayer; |
| sp<SurfaceControl> mChildLayer; |
| }; |
| |
| TEST_F(MirrorLayerTest, MirrorColorLayer) { |
| sp<SurfaceControl> grandchild = |
| createColorLayer("Grandchild layer", Color::BLUE, mChildLayer.get()); |
| Transaction() |
| .setFlags(grandchild, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque) |
| .setCrop(grandchild, Rect(0, 0, 200, 200)) |
| .show(grandchild) |
| .apply(); |
| |
| // Mirror mChildLayer |
| sp<SurfaceControl> mirrorLayer = mClient->mirrorSurface(mChildLayer.get()); |
| ASSERT_NE(mirrorLayer, nullptr); |
| |
| // Add mirrorLayer as child of mParentLayer so it's shown on the display |
| Transaction() |
| .reparent(mirrorLayer, mParentLayer) |
| .setPosition(mirrorLayer, 500, 500) |
| .show(mirrorLayer) |
| .apply(); |
| |
| { |
| SCOPED_TRACE("Initial Mirror"); |
| auto shot = screenshot(); |
| // Grandchild mirror |
| shot->expectColor(Rect(550, 550, 750, 750), Color::BLUE); |
| // Child mirror |
| shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN); |
| } |
| |
| // Set color to white on grandchild layer. |
| Transaction().setColor(grandchild, half3{1, 1, 1}).apply(); |
| { |
| SCOPED_TRACE("Updated Grandchild Layer Color"); |
| auto shot = screenshot(); |
| // Grandchild mirror |
| shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE); |
| // Child mirror |
| shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN); |
| } |
| |
| // Set color to black on child layer. |
| Transaction().setColor(mChildLayer, half3{0, 0, 0}).apply(); |
| { |
| SCOPED_TRACE("Updated Child Layer Color"); |
| auto shot = screenshot(); |
| // Grandchild mirror |
| shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE); |
| // Child mirror |
| shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK); |
| } |
| |
| // Remove grandchild layer |
| Transaction().reparent(grandchild, nullptr).apply(); |
| { |
| SCOPED_TRACE("Removed Grandchild Layer"); |
| auto shot = screenshot(); |
| // Grandchild mirror |
| shot->expectColor(Rect(550, 550, 750, 750), Color::BLACK); |
| // Child mirror |
| shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK); |
| } |
| |
| if (base::GetBoolProperty("debug.sf.enable_legacy_frontend", true)) { |
| GTEST_SKIP() << "Skipping test because mirroring behavior changes with legacy frontend"; |
| } |
| |
| // Remove child layer and verify we can still mirror the layer when |
| // its offscreen. |
| Transaction().reparent(mChildLayer, nullptr).apply(); |
| { |
| SCOPED_TRACE("Removed Child Layer"); |
| auto shot = screenshot(); |
| // Grandchild mirror |
| shot->expectColor(Rect(550, 550, 750, 750), Color::BLACK); |
| // Child mirror |
| shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK); |
| } |
| |
| // Add grandchild layer to offscreen layer |
| Transaction().reparent(grandchild, mChildLayer).apply(); |
| { |
| SCOPED_TRACE("Added Grandchild Layer"); |
| auto shot = screenshot(); |
| // Grandchild mirror |
| shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE); |
| // Child mirror |
| shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK); |
| } |
| |
| // Add child layer |
| Transaction().reparent(mChildLayer, mParentLayer).apply(); |
| { |
| SCOPED_TRACE("Added Child Layer"); |
| auto shot = screenshot(); |
| // Grandchild mirror |
| shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE); |
| // Child mirror |
| shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK); |
| } |
| } |
| |
| TEST_F(MirrorLayerTest, MirrorBufferLayer) { |
| sp<SurfaceControl> bufferQueueLayer = |
| createLayer("BufferQueueLayer", 200, 200, 0, mChildLayer.get()); |
| fillBufferQueueLayerColor(bufferQueueLayer, Color::BLUE, 200, 200); |
| Transaction().show(bufferQueueLayer).apply(); |
| |
| sp<SurfaceControl> mirrorLayer = mClient->mirrorSurface(mChildLayer.get()); |
| Transaction() |
| .reparent(mirrorLayer, mParentLayer) |
| .setPosition(mirrorLayer, 500, 500) |
| .show(mirrorLayer) |
| .apply(); |
| |
| { |
| SCOPED_TRACE("Initial Mirror BufferQueueLayer"); |
| auto shot = screenshot(); |
| // Buffer mirror |
| shot->expectColor(Rect(550, 550, 750, 750), Color::BLUE); |
| // Child mirror |
| shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN); |
| } |
| |
| fillBufferQueueLayerColor(bufferQueueLayer, Color::WHITE, 200, 200); |
| { |
| SCOPED_TRACE("Update BufferQueueLayer"); |
| auto shot = screenshot(); |
| // Buffer mirror |
| shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE); |
| // Child mirror |
| shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN); |
| } |
| |
| Transaction().reparent(bufferQueueLayer, nullptr).apply(); |
| { |
| SCOPED_TRACE("Removed BufferQueueLayer"); |
| auto shot = screenshot(); |
| // Buffer mirror |
| shot->expectColor(Rect(550, 550, 750, 750), Color::GREEN); |
| // Child mirror |
| shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN); |
| } |
| |
| sp<SurfaceControl> layer = |
| createLayer("Layer", 200, 200, ISurfaceComposerClient::eFXSurfaceBufferState, |
| mChildLayer.get()); |
| fillBufferLayerColor(layer, Color::BLUE, 200, 200); |
| Transaction().show(layer).apply(); |
| |
| { |
| SCOPED_TRACE("Initial Mirror Layer"); |
| auto shot = screenshot(); |
| // Buffer mirror |
| shot->expectColor(Rect(550, 550, 750, 750), Color::BLUE); |
| // Child mirror |
| shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN); |
| } |
| |
| fillBufferLayerColor(layer, Color::WHITE, 200, 200); |
| { |
| SCOPED_TRACE("Update Layer"); |
| auto shot = screenshot(); |
| // Buffer mirror |
| shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE); |
| // Child mirror |
| shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN); |
| } |
| |
| Transaction().reparent(layer, nullptr).apply(); |
| { |
| SCOPED_TRACE("Removed Layer"); |
| auto shot = screenshot(); |
| // Buffer mirror |
| shot->expectColor(Rect(550, 550, 750, 750), Color::GREEN); |
| // Child mirror |
| shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN); |
| } |
| } |
| |
| // Test that the mirror layer is initially offscreen. |
| TEST_F(MirrorLayerTest, InitialMirrorState) { |
| const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); |
| ASSERT_FALSE(ids.empty()); |
| |
| const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); |
| ui::DisplayMode mode; |
| SurfaceComposerClient::getActiveDisplayMode(display, &mode); |
| const ui::Size& size = mode.resolution; |
| |
| 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() |
| .setPosition(mirrorLayer, 500, 500) |
| .show(mirrorLayer) |
| .setLayer(mirrorLayer, INT32_MAX - 1) |
| .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(50, 50, 450, 450), Color::GREEN); |
| } |
| |
| // Add mirrorLayer as child of mParentLayer so it's shown on the display |
| Transaction().reparent(mirrorLayer, mParentLayer).apply(); |
| |
| { |
| SCOPED_TRACE("On Screen Mirror"); |
| auto shot = screenshot(); |
| // Child mirror |
| shot->expectColor(Rect(550, 550, 950, 950), Color::GREEN); |
| } |
| } |
| |
| // Test that a mirror layer can be screenshot when offscreen |
| TEST_F(MirrorLayerTest, OffscreenMirrorScreenshot) { |
| const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); |
| ASSERT_FALSE(ids.empty()); |
| const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); |
| 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(fillBufferLayerColor(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 |
| #pragma clang diagnostic pop // ignored "-Wconversion" |