Add captureLayers function to capture a layer and its children.
The captureLayers function gets a root layer as its argument.
It will capture the content for that layer and its descendants. The
capture will set the root layer's transform back to (0, 0).
Test: Transaction_test ScreenCaptureTest
Change-Id: I84fb66a65cd91434cddc99506b1924cf9f950935
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 9677125..973302c 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -122,6 +122,18 @@
return reply.readInt32();
}
+ virtual status_t captureLayers(const sp<IBinder>& layerHandleBinder,
+ const sp<IGraphicBufferProducer>& producer,
+ ISurfaceComposer::Rotation rotation) {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ data.writeStrongBinder(layerHandleBinder);
+ data.writeStrongBinder(IInterface::asBinder(producer));
+ data.writeInt32(static_cast<int32_t>(rotation));
+ remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply);
+ return reply.readInt32();
+ }
+
virtual bool authenticateSurfaceTexture(
const sp<IGraphicBufferProducer>& bufferProducer) const
{
@@ -588,6 +600,18 @@
reply->writeInt32(res);
return NO_ERROR;
}
+ case CAPTURE_LAYERS: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> layerHandleBinder = data.readStrongBinder();
+ sp<IGraphicBufferProducer> producer =
+ interface_cast<IGraphicBufferProducer>(data.readStrongBinder());
+ int32_t rotation = data.readInt32();
+
+ status_t res = captureLayers(layerHandleBinder, producer,
+ static_cast<ISurfaceComposer::Rotation>(rotation));
+ reply->writeInt32(res);
+ return NO_ERROR;
+ }
case AUTHENTICATE_SURFACE: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IGraphicBufferProducer> bufferProducer =
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 40e319e..15c4c9a 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -731,6 +731,15 @@
return ret;
}
+status_t ScreenshotClient::captureLayers(const sp<IBinder>& layerHandle,
+ const sp<IGraphicBufferProducer>& producer,
+ uint32_t rotation) {
+ sp<ISurfaceComposer> s(ComposerService::getComposerService());
+ if (s == NULL) return NO_INIT;
+ return s->captureLayers(layerHandle, producer,
+ static_cast<ISurfaceComposer::Rotation>(rotation));
+}
+
ScreenshotClient::ScreenshotClient()
: mHaveBuffer(false) {
memset(&mBuffer, 0, sizeof(mBuffer));
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index b226742..13e7473 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -174,6 +174,10 @@
bool useIdentityTransform,
Rotation rotation = eRotateNone) = 0;
+ virtual status_t captureLayers(const sp<IBinder>& layerHandleBinder,
+ const sp<IGraphicBufferProducer>& producer,
+ Rotation rotation = eRotateNone) = 0;
+
/* Clears the frame statistics for animations.
*
* Requires the ACCESS_SURFACE_FLINGER permission.
@@ -226,6 +230,7 @@
SET_ACTIVE_CONFIG,
CONNECT_DISPLAY,
CAPTURE_SCREEN,
+ CAPTURE_LAYERS,
CLEAR_ANIMATION_FRAME_STATS,
GET_ANIMATION_FRAME_STATS,
SET_POWER_MODE,
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 00d5936..d63dafe 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -297,6 +297,9 @@
bool useIdentityTransform,
uint32_t rotation,
sp<GraphicBuffer>* outbuffer);
+ static status_t captureLayers(const sp<IBinder>& layerHandle,
+ const sp<IGraphicBufferProducer>& producer, uint32_t rotation);
+
private:
mutable sp<CpuConsumer> mCpuConsumer;
mutable sp<IGraphicBufferProducer> mProducer;
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 572760e..660680b 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -529,6 +529,11 @@
int32_t /*minLayerZ*/, int32_t /*maxLayerZ*/,
bool /*useIdentityTransform*/,
Rotation /*rotation*/) override { return NO_ERROR; }
+ virtual status_t captureLayers(const sp<IBinder>& /*parentHandle*/,
+ const sp<IGraphicBufferProducer>& /*producer*/,
+ ISurfaceComposer::Rotation /*rotation*/) override {
+ return NO_ERROR;
+ }
status_t clearAnimationFrameStats() override { return NO_ERROR; }
status_t getAnimationFrameStats(FrameStats* /*outStats*/) const override {
return NO_ERROR;
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index d8152e0..d9bca04 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -40,6 +40,7 @@
RenderEngine/Texture.cpp \
RenderEngine/GLES20RenderEngine.cpp \
LayerProtoHelper.cpp \
+ RenderArea.cpp \
LOCAL_MODULE := libsurfaceflinger
LOCAL_C_INCLUDES := \
diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp
index 6923782..32526dd 100644
--- a/services/surfaceflinger/ColorLayer.cpp
+++ b/services/surfaceflinger/ColorLayer.cpp
@@ -40,13 +40,12 @@
: Layer(flinger, client, name, w, h, flags) {
}
-void ColorLayer::onDraw(const sp<const DisplayDevice>& hw,
- const Region& /* clip */, bool useIdentityTransform) const
-{
+void ColorLayer::onDraw(const RenderArea& renderArea, const Region& /* clip */,
+ bool useIdentityTransform) const {
const State& s(getDrawingState());
if (s.color.a>0) {
Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2);
- computeGeometry(hw, mesh, useIdentityTransform);
+ computeGeometry(renderArea, mesh, useIdentityTransform);
RenderEngine& engine(mFlinger->getRenderEngine());
engine.setupLayerBlending(getPremultipledAlpha(), false /* opaque */,
true /* disableTexture */, s.color);
diff --git a/services/surfaceflinger/ColorLayer.h b/services/surfaceflinger/ColorLayer.h
index ac3e2a9..cdf3eca 100644
--- a/services/surfaceflinger/ColorLayer.h
+++ b/services/surfaceflinger/ColorLayer.h
@@ -34,7 +34,7 @@
virtual ~ColorLayer() = default;
virtual const char* getTypeId() const { return "ColorLayer"; }
- virtual void onDraw(const sp<const DisplayDevice>& hw, const Region& clip,
+ virtual void onDraw(const RenderArea& renderArea, const Region& clip,
bool useIdentityTransform) const;
virtual bool isOpaque(const Layer::State&) const { return false; }
virtual bool isSecure() const { return false; }
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 8636e2a..49fef58 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -37,7 +37,9 @@
#include <utils/String8.h>
#include <utils/Timers.h>
+#include <gui/ISurfaceComposer.h>
#include <hardware/hwcomposer_defs.h>
+#include "RenderArea.h"
#ifdef USE_HWC2
#include <memory>
@@ -300,6 +302,35 @@
bool isSecure = false;
};
+class DisplayRenderArea : public RenderArea {
+public:
+ DisplayRenderArea(const sp<const DisplayDevice> device,
+ ISurfaceComposer::Rotation rotation = ISurfaceComposer::eRotateNone)
+ : DisplayRenderArea(device, device->getBounds(), device->getHeight(), device->getWidth(),
+ rotation) {}
+ DisplayRenderArea(const sp<const DisplayDevice> device, Rect sourceCrop, uint32_t reqHeight,
+ uint32_t reqWidth, ISurfaceComposer::Rotation rotation)
+ : RenderArea(reqHeight, reqWidth, rotation), mDevice(device), mSourceCrop(sourceCrop) {}
+
+ const Transform& getTransform() const override { return mDevice->getTransform(); }
+ Rect getBounds() const override { return mDevice->getBounds(); }
+ int getHeight() const override { return mDevice->getHeight(); }
+ int getWidth() const override { return mDevice->getWidth(); }
+ bool isSecure() const override { return mDevice->isSecure(); }
+ bool needsFiltering() const override { return mDevice->needsFiltering(); }
+ Rect getSourceCrop() const override { return mSourceCrop; }
+#ifdef USE_HWC2
+ bool getWideColorSupport() const override { return mDevice->getWideColorSupport(); }
+ android_color_mode_t getActiveColorMode() const override {
+ return mDevice->getActiveColorMode();
+ }
+#endif
+
+private:
+ const sp<const DisplayDevice> mDevice;
+ const Rect mSourceCrop;
+};
+
}; // namespace android
#endif // ANDROID_DISPLAY_DEVICE_H
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 27739ce..56328c9 100755
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1063,17 +1063,16 @@
// drawing...
// ---------------------------------------------------------------------------
-void Layer::draw(const sp<const DisplayDevice>& hw, const Region& clip) const {
- onDraw(hw, clip, false);
+void Layer::draw(const RenderArea& renderArea, const Region& clip) const {
+ onDraw(renderArea, clip, false);
}
-void Layer::draw(const sp<const DisplayDevice>& hw,
- bool useIdentityTransform) const {
- onDraw(hw, Region(hw->bounds()), useIdentityTransform);
+void Layer::draw(const RenderArea& renderArea, bool useIdentityTransform) const {
+ onDraw(renderArea, Region(renderArea.getBounds()), useIdentityTransform);
}
-void Layer::draw(const sp<const DisplayDevice>& hw) const {
- onDraw(hw, Region(hw->bounds()), false);
+void Layer::draw(const RenderArea& renderArea) const {
+ onDraw(renderArea, Region(renderArea.getBounds()), false);
}
static constexpr mat4 inverseOrientation(uint32_t transform) {
@@ -1097,7 +1096,7 @@
/*
* onDraw will draw the current layer onto the presentable buffer
*/
-void Layer::onDraw(const sp<const DisplayDevice>& hw, const Region& clip,
+void Layer::onDraw(const RenderArea& renderArea, const Region& clip,
bool useIdentityTransform) const
{
ATRACE_CALL();
@@ -1119,12 +1118,12 @@
finished = true;
return;
}
- under.orSelf( hw->getTransform().transform(layer->visibleRegion) );
+ under.orSelf(renderArea.getTransform().transform(layer->visibleRegion));
});
// if not everything below us is covered, we plug the holes!
Region holes(clip.subtract(under));
if (!holes.isEmpty()) {
- clearWithOpenGL(hw, 0, 0, 0, 1);
+ clearWithOpenGL(renderArea, 0, 0, 0, 1);
}
return;
}
@@ -1138,13 +1137,13 @@
// is probably going to have something visibly wrong.
}
- bool blackOutLayer = isProtected() || (isSecure() && !hw->isSecure());
+ bool blackOutLayer = isProtected() || (isSecure() && !renderArea.isSecure());
RenderEngine& engine(mFlinger->getRenderEngine());
if (!blackOutLayer) {
// TODO: we could be more subtle with isFixedSize()
- const bool useFiltering = getFiltering() || needsFiltering(hw) || isFixedSize();
+ const bool useFiltering = getFiltering() || needsFiltering(renderArea) || isFixedSize();
// Query the texture matrix given our current filtering mode.
float textureMatrix[16];
@@ -1190,31 +1189,29 @@
} else {
engine.setupLayerBlackedOut();
}
- drawWithOpenGL(hw, useIdentityTransform);
+ drawWithOpenGL(renderArea, useIdentityTransform);
engine.disableTexturing();
}
-void Layer::clearWithOpenGL(const sp<const DisplayDevice>& hw,
+void Layer::clearWithOpenGL(const RenderArea& renderArea,
float red, float green, float blue,
float alpha) const
{
RenderEngine& engine(mFlinger->getRenderEngine());
- computeGeometry(hw, mMesh, false);
+ computeGeometry(renderArea, mMesh, false);
engine.setupFillWithColor(red, green, blue, alpha);
engine.drawMesh(mMesh);
}
-void Layer::clearWithOpenGL(
- const sp<const DisplayDevice>& hw) const {
- clearWithOpenGL(hw, 0,0,0,0);
+void Layer::clearWithOpenGL(const RenderArea& renderArea) const {
+ clearWithOpenGL(renderArea, 0,0,0,0);
}
-void Layer::drawWithOpenGL(const sp<const DisplayDevice>& hw,
- bool useIdentityTransform) const {
+void Layer::drawWithOpenGL(const RenderArea& renderArea, bool useIdentityTransform) const {
const State& s(getDrawingState());
- computeGeometry(hw, mMesh, useIdentityTransform);
+ computeGeometry(renderArea, mMesh, useIdentityTransform);
/*
* NOTE: the way we compute the texture coordinates here produces
@@ -1440,12 +1437,11 @@
}
}
-void Layer::computeGeometry(const sp<const DisplayDevice>& hw, Mesh& mesh,
- bool useIdentityTransform) const
-{
+void Layer::computeGeometry(const RenderArea& renderArea, Mesh& mesh,
+ bool useIdentityTransform) const {
const Layer::State& s(getDrawingState());
- const Transform hwTransform(hw->getTransform());
- const uint32_t hw_h = hw->getHeight();
+ const Transform renderAreaTransform(renderArea.getTransform());
+ const uint32_t height = renderArea.getHeight();
Rect win = computeBounds();
vec2 lt = vec2(win.left, win.top);
@@ -1469,12 +1465,12 @@
}
Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
- position[0] = hwTransform.transform(lt);
- position[1] = hwTransform.transform(lb);
- position[2] = hwTransform.transform(rb);
- position[3] = hwTransform.transform(rt);
+ position[0] = renderAreaTransform.transform(lt);
+ position[1] = renderAreaTransform.transform(lb);
+ position[2] = renderAreaTransform.transform(rb);
+ position[3] = renderAreaTransform.transform(rt);
for (size_t i=0 ; i<4 ; i++) {
- position[i].y = hw_h - position[i].y;
+ position[i].y = height - position[i].y;
}
}
@@ -1512,8 +1508,8 @@
return !mCurrentCrop.isEmpty();
}
-bool Layer::needsFiltering(const sp<const DisplayDevice>& hw) const {
- return mNeedsFiltering || hw->needsFiltering();
+bool Layer::needsFiltering(const RenderArea& renderArea) const {
+ return mNeedsFiltering || renderArea.needsFiltering();
}
void Layer::setVisibleRegion(const Region& visibleRegion) {
@@ -2773,6 +2769,29 @@
}
}
+/**
+ * Traverse only children in z order, ignoring relative layers.
+ */
+void Layer::traverseChildrenInZOrder(LayerVector::StateSet stateSet,
+ const LayerVector::Visitor& visitor) {
+ const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
+ const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren;
+
+ size_t i = 0;
+ for (; i < children.size(); i++) {
+ const auto& relative = children[i];
+ if (relative->getZ() >= 0) {
+ break;
+ }
+ relative->traverseChildrenInZOrder(stateSet, visitor);
+ }
+ visitor(this);
+ for (; i < children.size(); i++) {
+ const auto& relative = children[i];
+ relative->traverseChildrenInZOrder(stateSet, visitor);
+ }
+}
+
Transform Layer::getTransform() const {
Transform t;
const auto& p = mDrawingParent.promote();
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 06c4863..d75e175 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -50,6 +50,7 @@
#include "RenderEngine/Mesh.h"
#include "RenderEngine/Texture.h"
#include <layerproto/LayerProtoHeader.h>
+#include "RenderArea.h"
#include <math/vec4.h>
@@ -255,8 +256,7 @@
return getLayerStack() == layerStack && (!mPrimaryDisplayOnly || isPrimaryDisplay);
}
- void computeGeometry(const sp<const DisplayDevice>& hw, Mesh& mesh,
- bool useIdentityTransform) const;
+ void computeGeometry(const RenderArea& renderArea, Mesh& mesh, bool useIdentityTransform) const;
Rect computeBounds(const Region& activeTransparentRegion) const;
Rect computeBounds() const;
@@ -312,7 +312,7 @@
/*
* onDraw - draws the surface.
*/
- virtual void onDraw(const sp<const DisplayDevice>& hw, const Region& clip,
+ virtual void onDraw(const RenderArea& renderArea, const Region& clip,
bool useIdentityTransform) const;
public:
@@ -379,9 +379,9 @@
* draw - performs some global clipping optimizations
* and calls onDraw().
*/
- void draw(const sp<const DisplayDevice>& hw, const Region& clip) const;
- void draw(const sp<const DisplayDevice>& hw, bool useIdentityTransform) const;
- void draw(const sp<const DisplayDevice>& hw) const;
+ void draw(const RenderArea& renderArea, const Region& clip) const;
+ void draw(const RenderArea& renderArea, bool useIdentityTransform) const;
+ void draw(const RenderArea& renderArea) const;
/*
* doTransaction - process the transaction. This is a good place to figure
@@ -472,7 +472,7 @@
#endif
// -----------------------------------------------------------------------
- void clearWithOpenGL(const sp<const DisplayDevice>& hw) const;
+ void clearWithOpenGL(const RenderArea& renderArea) const;
void setFiltering(bool filtering);
bool getFiltering() const;
@@ -516,6 +516,9 @@
const LayerVector::Visitor& visitor);
void traverseInZOrder(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
+ void traverseChildrenInZOrder(LayerVector::StateSet stateSet,
+ const LayerVector::Visitor& visitor);
+
size_t getChildrenCount() const;
void addChild(const sp<Layer>& layer);
// Returns index if removed, or negative value otherwise
@@ -569,7 +572,7 @@
void commitTransaction(const State& stateToCommit);
// needsLinearFiltering - true if this surface's state requires filtering
- bool needsFiltering(const sp<const DisplayDevice>& hw) const;
+ bool needsFiltering(const RenderArea& renderArea) const;
uint32_t getEffectiveUsage(uint32_t usage) const;
@@ -582,9 +585,9 @@
static bool getOpacityForFormat(uint32_t format);
// drawing
- void clearWithOpenGL(const sp<const DisplayDevice>& hw,
+ void clearWithOpenGL(const RenderArea& renderArea,
float r, float g, float b, float alpha) const;
- void drawWithOpenGL(const sp<const DisplayDevice>& hw,
+ void drawWithOpenGL(const RenderArea& renderArea,
bool useIdentityTransform) const;
// Temporary - Used only for LEGACY camera mode.
diff --git a/services/surfaceflinger/RenderArea.cpp b/services/surfaceflinger/RenderArea.cpp
new file mode 100644
index 0000000..6225df1
--- /dev/null
+++ b/services/surfaceflinger/RenderArea.cpp
@@ -0,0 +1,34 @@
+#include "RenderArea.h"
+
+namespace android {
+
+/*
+ * Checks that the requested width and height are valid and updates them to the render area
+ * dimensions if they are set to 0
+ */
+status_t RenderArea::updateDimensions() {
+ // get screen geometry
+
+ uint32_t width = getWidth();
+ uint32_t height = getHeight();
+
+ if (mRotationFlags & Transform::ROT_90) {
+ std::swap(width, height);
+ }
+
+ if ((mReqWidth > width) || (mReqHeight > height)) {
+ ALOGE("size mismatch (%d, %d) > (%d, %d)", mReqWidth, mReqHeight, width, height);
+ return BAD_VALUE;
+ }
+
+ if (mReqWidth == 0) {
+ mReqWidth = width;
+ }
+ if (mReqHeight == 0) {
+ mReqHeight = height;
+ }
+
+ return NO_ERROR;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
new file mode 100644
index 0000000..faf1ec6
--- /dev/null
+++ b/services/surfaceflinger/RenderArea.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#include "Transform.h"
+
+namespace android {
+
+class RenderArea {
+public:
+ RenderArea(uint32_t reqHeight, uint32_t reqWidth, ISurfaceComposer::Rotation rotation)
+ : mReqHeight(reqHeight), mReqWidth(reqWidth) {
+ mRotationFlags = Transform::fromRotation(rotation);
+ }
+
+ virtual ~RenderArea() = default;
+
+ virtual const Transform& getTransform() const = 0;
+ virtual Rect getBounds() const = 0;
+ virtual int getHeight() const = 0;
+ virtual int getWidth() const = 0;
+ virtual bool isSecure() const = 0;
+ virtual bool needsFiltering() const = 0;
+ virtual Rect getSourceCrop() const = 0;
+
+ int getReqHeight() const { return mReqHeight; };
+ int getReqWidth() const { return mReqWidth; };
+ Transform::orientation_flags getRotationFlags() const { return mRotationFlags; };
+#ifdef USE_HWC2
+ virtual bool getWideColorSupport() const = 0;
+ virtual android_color_mode_t getActiveColorMode() const = 0;
+#endif
+
+ status_t updateDimensions();
+
+private:
+ uint32_t mReqHeight;
+ uint32_t mReqWidth;
+ Transform::orientation_flags mRotationFlags;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 05c76fc..fe9409b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2650,6 +2650,7 @@
{
ALOGV("doComposeSurfaces");
+ const DisplayRenderArea renderArea(displayDevice);
const auto hwcId = displayDevice->getHwcDisplayId();
mat4 oldColorMatrix;
@@ -2759,12 +2760,12 @@
&& hasClientComposition) {
// never clear the very first layer since we're
// guaranteed the FB is already cleared
- layer->clearWithOpenGL(displayDevice);
+ layer->clearWithOpenGL(renderArea);
}
break;
}
case HWC2::Composition::Client: {
- layer->draw(displayDevice, clip);
+ layer->draw(renderArea, clip);
break;
}
default:
@@ -2781,7 +2782,7 @@
const Region clip(dirty.intersect(
displayTransform.transform(layer->visibleRegion)));
if (!clip.isEmpty()) {
- layer->draw(displayDevice, clip);
+ layer->draw(renderArea, clip);
}
}
}
@@ -4029,6 +4030,17 @@
}
break;
}
+ case CAPTURE_LAYERS: {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ if ((uid != AID_GRAPHICS) &&
+ !PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) {
+ ALOGE("Permission Denial: can't read framebuffer pid=%d, uid=%d", pid, uid);
+ return PERMISSION_DENIED;
+ }
+ break;
+ }
}
return OK;
}
@@ -4228,35 +4240,6 @@
repaintEverythingLocked();
}
-// Checks that the requested width and height are valid and updates them to the display dimensions
-// if they are set to 0
-static status_t updateDimensionsLocked(const sp<const DisplayDevice>& displayDevice,
- Transform::orientation_flags rotation,
- uint32_t* requestedWidth, uint32_t* requestedHeight) {
- // get screen geometry
- uint32_t displayWidth = displayDevice->getWidth();
- uint32_t displayHeight = displayDevice->getHeight();
-
- if (rotation & Transform::ROT_90) {
- std::swap(displayWidth, displayHeight);
- }
-
- if ((*requestedWidth > displayWidth) || (*requestedHeight > displayHeight)) {
- ALOGE("size mismatch (%d, %d) > (%d, %d)",
- *requestedWidth, *requestedHeight, displayWidth, displayHeight);
- return BAD_VALUE;
- }
-
- if (*requestedWidth == 0) {
- *requestedWidth = displayWidth;
- }
- if (*requestedHeight == 0) {
- *requestedHeight = displayHeight;
- }
-
- return NO_ERROR;
-}
-
// A simple RAII class to disconnect from an ANativeWindow* when it goes out of scope
class WindowDisconnector {
public:
@@ -4305,50 +4288,86 @@
}
status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display,
- const sp<IGraphicBufferProducer>& producer,
- Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- int32_t minLayerZ, int32_t maxLayerZ,
- bool useIdentityTransform, ISurfaceComposer::Rotation rotation) {
+ const sp<IGraphicBufferProducer>& producer, Rect sourceCrop,
+ uint32_t reqWidth, uint32_t reqHeight, int32_t minLayerZ,
+ int32_t maxLayerZ, bool useIdentityTransform,
+ ISurfaceComposer::Rotation rotation) {
ATRACE_CALL();
- if (CC_UNLIKELY(display == 0))
- return BAD_VALUE;
+ if (CC_UNLIKELY(display == 0)) return BAD_VALUE;
+
+ const sp<const DisplayDevice> device(getDisplayDeviceLocked(display));
+ DisplayRenderArea renderArea(device, sourceCrop, reqHeight, reqWidth, rotation);
+
+ auto traverseLayers = std::bind(std::mem_fn(&SurfaceFlinger::traverseLayersInDisplay), this,
+ device, minLayerZ, maxLayerZ, std::placeholders::_1);
+ return captureScreenCommon(renderArea, traverseLayers, producer, useIdentityTransform);
+}
+
+status_t SurfaceFlinger::captureLayers(const sp<IBinder>& layerHandleBinder,
+ const sp<IGraphicBufferProducer>& producer,
+ ISurfaceComposer::Rotation rotation) {
+ ATRACE_CALL();
+
+ class LayerRenderArea : public RenderArea {
+ public:
+ LayerRenderArea(const sp<Layer>& layer, ISurfaceComposer::Rotation rotation)
+ : RenderArea(layer->getCurrentState().active.h, layer->getCurrentState().active.w,
+ rotation),
+ mLayer(layer) {}
+ const Transform& getTransform() const override {
+ // Make the top level transform the inverse the transform and it's parent so it sets
+ // the whole capture back to 0,0
+ return *new Transform(mLayer->getTransform().inverse());
+ }
+ Rect getBounds() const override {
+ const Layer::State& layerState(mLayer->getDrawingState());
+ return Rect(layerState.active.w, layerState.active.h);
+ }
+ int getHeight() const override { return mLayer->getDrawingState().active.h; }
+ int getWidth() const override { return mLayer->getDrawingState().active.w; }
+ bool isSecure() const override { return false; }
+ bool needsFiltering() const override { return false; }
+
+ Rect getSourceCrop() const override { return getBounds(); }
+ bool getWideColorSupport() const override { return false; }
+ android_color_mode_t getActiveColorMode() const override { return HAL_COLOR_MODE_NATIVE; }
+
+ private:
+ const sp<Layer>& mLayer;
+ };
+
+ auto layerHandle = reinterpret_cast<Layer::Handle*>(layerHandleBinder.get());
+ auto parent = layerHandle->owner.promote();
+
+ LayerRenderArea renderArea(parent, rotation);
+ auto traverseLayers = [parent](const LayerVector::Visitor& visitor) {
+ parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
+ if (!layer->isVisible()) {
+ return;
+ }
+ visitor(layer);
+ });
+ };
+ return captureScreenCommon(renderArea, traverseLayers, producer, false);
+}
+
+status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea,
+ TraverseLayersFunction traverseLayers,
+ const sp<IGraphicBufferProducer>& producer,
+ bool useIdentityTransform) {
+ ATRACE_CALL();
if (CC_UNLIKELY(producer == 0))
return BAD_VALUE;
+ renderArea.updateDimensions();
+
// if we have secure windows on this display, never allow the screen capture
// unless the producer interface is local (i.e.: we can take a screenshot for
// ourselves).
bool isLocalScreenshot = IInterface::asBinder(producer)->localBinder();
- // Convert to surfaceflinger's internal rotation type.
- Transform::orientation_flags rotationFlags;
- switch (rotation) {
- case ISurfaceComposer::eRotateNone:
- rotationFlags = Transform::ROT_0;
- break;
- case ISurfaceComposer::eRotate90:
- rotationFlags = Transform::ROT_90;
- break;
- case ISurfaceComposer::eRotate180:
- rotationFlags = Transform::ROT_180;
- break;
- case ISurfaceComposer::eRotate270:
- rotationFlags = Transform::ROT_270;
- break;
- default:
- rotationFlags = Transform::ROT_0;
- ALOGE("Invalid rotation passed to captureScreen(): %d\n", rotation);
- break;
- }
-
- { // Autolock scope
- Mutex::Autolock lock(mStateLock);
- sp<const DisplayDevice> displayDevice(getDisplayDeviceLocked(display));
- updateDimensionsLocked(displayDevice, rotationFlags, &reqWidth, &reqHeight);
- }
-
// create a surface (because we're a producer, and we need to
// dequeue/queue a buffer)
sp<Surface> surface = new Surface(producer, false);
@@ -4369,9 +4388,9 @@
WindowDisconnector disconnector(window, NATIVE_WINDOW_API_EGL);
ANativeWindowBuffer* buffer = nullptr;
- result = getWindowBuffer(window, reqWidth, reqHeight,
- hasWideColorDisplay && !mForceNativeColorMode,
- getRenderEngine().usesWideColor(), &buffer);
+ result = getWindowBuffer(window, renderArea.getReqWidth(), renderArea.getReqHeight(),
+ hasWideColorDisplay && !mForceNativeColorMode,
+ getRenderEngine().usesWideColor(), &buffer);
if (result != NO_ERROR) {
return result;
}
@@ -4399,10 +4418,8 @@
int fd = -1;
{
Mutex::Autolock _l(mStateLock);
- sp<const DisplayDevice> device(getDisplayDeviceLocked(display));
- result = captureScreenImplLocked(device, buffer, sourceCrop, reqWidth, reqHeight,
- minLayerZ, maxLayerZ, useIdentityTransform,
- rotationFlags, isLocalScreenshot, &fd);
+ result = captureScreenImplLocked(renderArea, traverseLayers, buffer,
+ useIdentityTransform, isLocalScreenshot, &fd);
}
{
@@ -4431,84 +4448,66 @@
// queueBuffer takes ownership of syncFd
result = window->queueBuffer(window, buffer, syncFd);
}
-
return result;
}
-
-void SurfaceFlinger::renderScreenImplLocked(
- const sp<const DisplayDevice>& hw,
- Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- int32_t minLayerZ, int32_t maxLayerZ,
- bool yswap, bool useIdentityTransform, Transform::orientation_flags rotation)
-{
+void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea,
+ TraverseLayersFunction traverseLayers, bool yswap,
+ bool useIdentityTransform) {
ATRACE_CALL();
+
RenderEngine& engine(getRenderEngine());
// get screen geometry
- const int32_t hw_w = hw->getWidth();
- const int32_t hw_h = hw->getHeight();
- const bool filtering = static_cast<int32_t>(reqWidth) != hw_w ||
- static_cast<int32_t>(reqHeight) != hw_h;
+ const auto raWidth = renderArea.getWidth();
+ const auto raHeight = renderArea.getHeight();
+
+ const auto reqWidth = renderArea.getReqWidth();
+ const auto reqHeight = renderArea.getReqHeight();
+ Rect sourceCrop = renderArea.getSourceCrop();
+
+ const bool filtering = static_cast<int32_t>(reqWidth) != raWidth ||
+ static_cast<int32_t>(reqHeight) != raHeight;
// if a default or invalid sourceCrop is passed in, set reasonable values
- if (sourceCrop.width() == 0 || sourceCrop.height() == 0 ||
- !sourceCrop.isValid()) {
+ if (sourceCrop.width() == 0 || sourceCrop.height() == 0 || !sourceCrop.isValid()) {
sourceCrop.setLeftTop(Point(0, 0));
- sourceCrop.setRightBottom(Point(hw_w, hw_h));
+ sourceCrop.setRightBottom(Point(raWidth, raHeight));
}
// ensure that sourceCrop is inside screen
if (sourceCrop.left < 0) {
ALOGE("Invalid crop rect: l = %d (< 0)", sourceCrop.left);
}
- if (sourceCrop.right > hw_w) {
- ALOGE("Invalid crop rect: r = %d (> %d)", sourceCrop.right, hw_w);
+ if (sourceCrop.right > raWidth) {
+ ALOGE("Invalid crop rect: r = %d (> %d)", sourceCrop.right, raWidth);
}
if (sourceCrop.top < 0) {
ALOGE("Invalid crop rect: t = %d (< 0)", sourceCrop.top);
}
- if (sourceCrop.bottom > hw_h) {
- ALOGE("Invalid crop rect: b = %d (> %d)", sourceCrop.bottom, hw_h);
+ if (sourceCrop.bottom > raHeight) {
+ ALOGE("Invalid crop rect: b = %d (> %d)", sourceCrop.bottom, raHeight);
}
-#ifdef USE_HWC2
- engine.setWideColor(hw->getWideColorSupport() && !mForceNativeColorMode);
- engine.setColorMode(mForceNativeColorMode ? HAL_COLOR_MODE_NATIVE : hw->getActiveColorMode());
-#endif
+ engine.setWideColor(renderArea.getWideColorSupport() && !mForceNativeColorMode);
+ engine.setColorMode(mForceNativeColorMode ? HAL_COLOR_MODE_NATIVE : renderArea.getActiveColorMode());
// make sure to clear all GL error flags
engine.checkErrors();
// set-up our viewport
- engine.setViewportAndProjection(
- reqWidth, reqHeight, sourceCrop, hw_h, yswap, rotation);
+ engine.setViewportAndProjection(reqWidth, reqHeight, sourceCrop, raHeight, yswap,
+ renderArea.getRotationFlags());
engine.disableTexturing();
// redraw the screen entirely...
engine.clearWithColor(0, 0, 0, 1);
- // We loop through the first level of layers without traversing,
- // as we need to interpret min/max layer Z in the top level Z space.
- for (const auto& layer : mDrawingState.layersSortedByZ) {
- if (!layer->belongsToDisplay(hw->getLayerStack(), false)) {
- continue;
- }
- const Layer::State& state(layer->getDrawingState());
- if (state.z < minLayerZ || state.z > maxLayerZ) {
- continue;
- }
- layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
- if (!layer->isVisible()) {
- return;
- }
- if (filtering) layer->setFiltering(true);
- layer->draw(hw, useIdentityTransform);
- if (filtering) layer->setFiltering(false);
- });
- }
-
- hw->setViewportAndProjection();
+ traverseLayers([&](Layer* layer) {
+ if (filtering) layer->setFiltering(true);
+ layer->draw(renderArea, useIdentityTransform);
+ if (filtering) layer->setFiltering(false);
+ });
}
// A simple RAII class that holds an EGLImage and destroys it either:
@@ -4531,27 +4530,18 @@
EGLImageKHR mImage;
};
-status_t SurfaceFlinger::captureScreenImplLocked(const sp<const DisplayDevice>& hw,
- ANativeWindowBuffer* buffer, Rect sourceCrop,
- uint32_t reqWidth, uint32_t reqHeight,
- int32_t minLayerZ, int32_t maxLayerZ,
- bool useIdentityTransform,
- Transform::orientation_flags rotation,
- bool isLocalScreenshot, int* outSyncFd) {
+status_t SurfaceFlinger::captureScreenImplLocked(const RenderArea& renderArea,
+ TraverseLayersFunction traverseLayers,
+ ANativeWindowBuffer* buffer,
+ bool useIdentityTransform, bool isLocalScreenshot,
+ int* outSyncFd) {
ATRACE_CALL();
bool secureLayerIsVisible = false;
- for (const auto& layer : mDrawingState.layersSortedByZ) {
- const Layer::State& state(layer->getDrawingState());
- if (!layer->belongsToDisplay(hw->getLayerStack(), false) ||
- (state.z < minLayerZ || state.z > maxLayerZ)) {
- continue;
- }
- layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer *layer) {
- secureLayerIsVisible = secureLayerIsVisible || (layer->isVisible() &&
- layer->isSecure());
- });
- }
+
+ traverseLayers([&](Layer* layer) {
+ secureLayerIsVisible = secureLayerIsVisible || (layer->isVisible() && layer->isSecure());
+ });
if (!isLocalScreenshot && secureLayerIsVisible) {
ALOGW("FB is protected: PERMISSION_DENIED");
@@ -4582,9 +4572,7 @@
// via an FBO, which means we didn't have to create
// an EGLSurface and therefore we're not
// dependent on the context's EGLConfig.
- renderScreenImplLocked(
- hw, sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ, true,
- useIdentityTransform, rotation);
+ renderScreenImplLocked(renderArea, traverseLayers, true, useIdentityTransform);
// Attempt to create a sync khr object that can produce a sync point. If that
// isn't available, create a non-dupable sync object in the fallback path and
@@ -4625,46 +4613,40 @@
*outSyncFd = syncFd;
if (DEBUG_SCREENSHOTS) {
+ const auto reqWidth = renderArea.getReqWidth();
+ const auto reqHeight = renderArea.getReqHeight();
+
uint32_t* pixels = new uint32_t[reqWidth*reqHeight];
getRenderEngine().readPixels(0, 0, reqWidth, reqHeight, pixels);
- checkScreenshot(reqWidth, reqHeight, reqWidth, pixels,
- hw, minLayerZ, maxLayerZ);
+ checkScreenshot(reqWidth, reqHeight, reqWidth, pixels, traverseLayers);
delete [] pixels;
}
// destroy our image
imageHolder.destroy();
-
return NO_ERROR;
}
void SurfaceFlinger::checkScreenshot(size_t w, size_t s, size_t h, void const* vaddr,
- const sp<const DisplayDevice>& hw, int32_t minLayerZ, int32_t maxLayerZ) {
+ TraverseLayersFunction traverseLayers) {
if (DEBUG_SCREENSHOTS) {
- for (size_t y=0 ; y<h ; y++) {
- uint32_t const * p = (uint32_t const *)vaddr + y*s;
- for (size_t x=0 ; x<w ; x++) {
+ for (size_t y = 0; y < h; y++) {
+ uint32_t const* p = (uint32_t const*)vaddr + y * s;
+ for (size_t x = 0; x < w; x++) {
if (p[x] != 0xFF000000) return;
}
}
- ALOGE("*** we just took a black screenshot ***\n"
- "requested minz=%d, maxz=%d, layerStack=%d",
- minLayerZ, maxLayerZ, hw->getLayerStack());
+ ALOGE("*** we just took a black screenshot ***");
size_t i = 0;
- for (const auto& layer : mDrawingState.layersSortedByZ) {
+ traverseLayers([&](Layer* layer) {
const Layer::State& state(layer->getDrawingState());
- if (layer->belongsToDisplay(hw->getLayerStack(), false) && state.z >= minLayerZ &&
- state.z <= maxLayerZ) {
- layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
- ALOGE("%c index=%zu, name=%s, layerStack=%d, z=%d, visible=%d, flags=%x, alpha=%.3f",
- layer->isVisible() ? '+' : '-',
- i, layer->getName().string(), layer->getLayerStack(), state.z,
- layer->isVisible(), state.flags, static_cast<float>(state.color.a));
- i++;
- });
- }
- }
+ ALOGE("%c index=%zu, name=%s, layerStack=%d, z=%d, visible=%d, flags=%x, alpha=%.3f",
+ layer->isVisible() ? '+' : '-', i, layer->getName().string(),
+ layer->getLayerStack(), state.z, layer->isVisible(), state.flags,
+ static_cast<float>(state.color.a));
+ i++;
+ });
}
}
@@ -4678,6 +4660,28 @@
layersSortedByZ.traverseInReverseZOrder(stateSet, visitor);
}
+void SurfaceFlinger::traverseLayersInDisplay(const sp<const DisplayDevice>& hw, int32_t minLayerZ,
+ int32_t maxLayerZ,
+ const LayerVector::Visitor& visitor) {
+ // We loop through the first level of layers without traversing,
+ // as we need to interpret min/max layer Z in the top level Z space.
+ for (const auto& layer : mDrawingState.layersSortedByZ) {
+ if (!layer->belongsToDisplay(hw->getLayerStack(), false)) {
+ continue;
+ }
+ const Layer::State& state(layer->getDrawingState());
+ if (state.z < minLayerZ || state.z > maxLayerZ) {
+ continue;
+ }
+ layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
+ if (!layer->isVisible()) {
+ return;
+ }
+ visitor(layer);
+ });
+ }
+}
+
}; // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index bd98c8f..9975de9 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -77,6 +77,7 @@
#include <string>
#include <thread>
#include <utility>
+#include "RenderArea.h"
#include <layerproto/LayerProtoHeader.h>
@@ -97,6 +98,8 @@
class VSyncSource;
class InjectVSyncSource;
+typedef std::function<void(const LayerVector::Visitor&)> TraverseLayersFunction;
+
namespace dvr {
class VrFlinger;
} // namespace dvr
@@ -303,6 +306,9 @@
Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
int32_t minLayerZ, int32_t maxLayerZ,
bool useIdentityTransform, ISurfaceComposer::Rotation rotation);
+ virtual status_t captureLayers(const sp<IBinder>& parentHandle,
+ const sp<IGraphicBufferProducer>& producer,
+ ISurfaceComposer::Rotation rotation);
virtual status_t getDisplayStats(const sp<IBinder>& display,
DisplayStatInfo* stats);
virtual status_t getDisplayConfigs(const sp<IBinder>& display,
@@ -448,28 +454,26 @@
void startBootAnim();
- void renderScreenImplLocked(
- const sp<const DisplayDevice>& hw,
- Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- int32_t minLayerZ, int32_t maxLayerZ,
- bool yswap, bool useIdentityTransform, Transform::orientation_flags rotation);
+ void renderScreenImplLocked(const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
+ bool yswap, bool useIdentityTransform);
+
+ status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers,
+ const sp<IGraphicBufferProducer>& producer,
+ bool useIdentityTransform);
#ifdef USE_HWC2
- status_t captureScreenImplLocked(const sp<const DisplayDevice>& device,
- ANativeWindowBuffer* buffer, Rect sourceCrop,
- uint32_t reqWidth, uint32_t reqHeight, int32_t minLayerZ,
- int32_t maxLayerZ, bool useIdentityTransform,
- Transform::orientation_flags rotation, bool isLocalScreenshot,
- int* outSyncFd);
+ status_t captureScreenImplLocked(const RenderArea& renderArea,
+ TraverseLayersFunction traverseLayers,
+ ANativeWindowBuffer* buffer, bool useIdentityTransform,
+ bool isLocalScreenshot, int* outSyncFd);
#else
- status_t captureScreenImplLocked(
- const sp<const DisplayDevice>& hw,
- const sp<IGraphicBufferProducer>& producer,
- Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- int32_t minLayerZ, int32_t maxLayerZ,
- bool useIdentityTransform, Transform::orientation_flags rotation,
- bool isLocalScreenshot);
+ status_t captureScreenImplLocked(const RenderArea& renderArea,
+ TraverseLayersFunction traverseLayers,
+ const sp<IGraphicBufferProducer>& producer,
+ bool useIdentityTransform, bool isLocalScreenshot);
#endif
+ void traverseLayersInDisplay(const sp<const DisplayDevice>& display, int32_t minLayerZ,
+ int32_t maxLayerZ, const LayerVector::Visitor& visitor);
sp<StartPropertySetThread> mStartPropertySetThread = nullptr;
@@ -613,8 +617,7 @@
bool startDdmConnection();
void appendSfConfigString(String8& result) const;
void checkScreenshot(size_t w, size_t s, size_t h, void const* vaddr,
- const sp<const DisplayDevice>& hw,
- int32_t minLayerZ, int32_t maxLayerZ);
+ TraverseLayersFunction traverseLayers);
void logFrameStats();
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index ed7641f..8c530e0 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -2212,6 +2212,8 @@
bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& hw, const Region& dirty)
{
+ DisplayRenderArea renderArea(hw);
+
RenderEngine& engine(getRenderEngine());
const int32_t id = hw->getHwcDisplayId();
HWComposer& hwc(getHwComposer());
@@ -2303,12 +2305,12 @@
&& hasGlesComposition) {
// never clear the very first layer since we're
// guaranteed the FB is already cleared
- layer->clearWithOpenGL(hw);
+ layer->clearWithOpenGL(renderArea);
}
break;
}
case HWC_FRAMEBUFFER: {
- layer->draw(hw, clip);
+ layer->draw(renderArea, clip);
break;
}
case HWC_FRAMEBUFFER_TARGET: {
@@ -2328,7 +2330,7 @@
const Region clip(dirty.intersect(
tr.transform(layer->visibleRegion)));
if (!clip.isEmpty()) {
- layer->draw(hw, clip);
+ layer->draw(renderArea, clip);
}
}
}
@@ -3493,6 +3495,18 @@
}
break;
}
+ case CAPTURE_LAYERS:
+ {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ if ((uid != AID_GRAPHICS) &&
+ !PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) {
+ ALOGE("Permission Denial: can't read framebuffer pid=%d, uid=%d", pid, uid);
+ return PERMISSION_DENIED;
+ }
+ break;
+ }
}
status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags);
@@ -3766,16 +3780,72 @@
}
};
-
status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display,
- const sp<IGraphicBufferProducer>& producer,
- Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- int32_t minLayerZ, int32_t maxLayerZ,
- bool useIdentityTransform, ISurfaceComposer::Rotation rotation) {
-
+ const sp<IGraphicBufferProducer>& producer,
+ Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
+ int32_t minLayerZ, int32_t maxLayerZ,
+ bool useIdentityTransform, ISurfaceComposer::Rotation rotation) {
+ ATRACE_CALL();
if (CC_UNLIKELY(display == 0))
return BAD_VALUE;
+ const sp<const DisplayDevice> device(getDisplayDeviceLocked(display));
+ DisplayRenderArea renderArea(device, sourceCrop, reqHeight, reqWidth, rotation);
+
+ auto traverseLayers = std::bind(std::mem_fn(&SurfaceFlinger::traverseLayersInDisplay), this,
+ device, minLayerZ, maxLayerZ, std::placeholders::_1);
+ return captureScreenCommon(renderArea, traverseLayers, producer, useIdentityTransform);
+}
+
+status_t SurfaceFlinger::captureLayers(const sp<IBinder>& layerHandleBinder,
+ const sp<IGraphicBufferProducer>& producer,
+ ISurfaceComposer::Rotation rotation) {
+ ATRACE_CALL();
+ class LayerRenderArea : public RenderArea {
+ public:
+ LayerRenderArea(const sp<Layer>& layer, ISurfaceComposer::Rotation rotation)
+ : RenderArea(layer->getCurrentState().active.h, layer->getCurrentState().active.w,
+ rotation),
+ mLayer(layer) {}
+ const Transform& getTransform() const override {
+ // Make the top level transform the inverse the transform and it's parent so it sets
+ // the whole capture back to 0,0
+ return *new Transform(mLayer->getTransform().inverse());
+ }
+ Rect getBounds() const override {
+ const Layer::State& layerState(mLayer->getDrawingState());
+ return Rect(layerState.active.w, layerState.active.h);
+ }
+ int getHeight() const override { return mLayer->getDrawingState().active.h; }
+ int getWidth() const override { return mLayer->getDrawingState().active.w; }
+ bool isSecure() const override { return false; }
+ bool needsFiltering() const override { return false; }
+
+ Rect getSourceCrop() const override { return getBounds(); }
+
+ private:
+ const sp<Layer>& mLayer;
+ };
+
+ auto layerHandle = reinterpret_cast<Layer::Handle*>(layerHandleBinder.get());
+ auto parent = layerHandle->owner.promote();
+
+ LayerRenderArea renderArea(parent, rotation);
+ auto traverseLayers = [parent](const LayerVector::Visitor& visitor) {
+ parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
+ if (!layer->isVisible()) {
+ return;
+ }
+ visitor(layer);
+ });
+ };
+ return captureScreenCommon(renderArea, traverseLayers, producer, false);
+}
+
+status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea,
+ TraverseLayersFunction traverseLayers,
+ const sp<IGraphicBufferProducer>& producer,
+ bool useIdentityTransform) {
if (CC_UNLIKELY(producer == 0))
return BAD_VALUE;
@@ -3784,64 +3854,33 @@
// ourselves).
bool isLocalScreenshot = IInterface::asBinder(producer)->localBinder();
- // Convert to surfaceflinger's internal rotation type.
- Transform::orientation_flags rotationFlags;
- switch (rotation) {
- case ISurfaceComposer::eRotateNone:
- rotationFlags = Transform::ROT_0;
- break;
- case ISurfaceComposer::eRotate90:
- rotationFlags = Transform::ROT_90;
- break;
- case ISurfaceComposer::eRotate180:
- rotationFlags = Transform::ROT_180;
- break;
- case ISurfaceComposer::eRotate270:
- rotationFlags = Transform::ROT_270;
- break;
- default:
- rotationFlags = Transform::ROT_0;
- ALOGE("Invalid rotation passed to captureScreen(): %d\n", rotation);
- break;
- }
-
class MessageCaptureScreen : public MessageBase {
SurfaceFlinger* flinger;
- sp<IBinder> display;
+ const RenderArea* renderArea;
+ TraverseLayersFunction traverseLayers;
sp<IGraphicBufferProducer> producer;
- Rect sourceCrop;
- uint32_t reqWidth, reqHeight;
- int32_t minLayerZ,maxLayerZ;
bool useIdentityTransform;
- Transform::orientation_flags rotation;
status_t result;
bool isLocalScreenshot;
public:
- MessageCaptureScreen(SurfaceFlinger* flinger,
- const sp<IBinder>& display,
- const sp<IGraphicBufferProducer>& producer,
- Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- int32_t minLayerZ, int32_t maxLayerZ,
- bool useIdentityTransform,
- Transform::orientation_flags rotation,
- bool isLocalScreenshot)
- : flinger(flinger), display(display), producer(producer),
- sourceCrop(sourceCrop), reqWidth(reqWidth), reqHeight(reqHeight),
- minLayerZ(minLayerZ), maxLayerZ(maxLayerZ),
- useIdentityTransform(useIdentityTransform),
- rotation(rotation), result(PERMISSION_DENIED),
- isLocalScreenshot(isLocalScreenshot)
- {
- }
+ MessageCaptureScreen(SurfaceFlinger* flinger, const RenderArea* renderArea,
+ TraverseLayersFunction traverseLayers,
+ const sp<IGraphicBufferProducer>& producer, bool useIdentityTransform,
+ bool isLocalScreenshot)
+ : flinger(flinger),
+ renderArea(renderArea),
+ traverseLayers(traverseLayers),
+ producer(producer),
+ useIdentityTransform(useIdentityTransform),
+ result(PERMISSION_DENIED),
+ isLocalScreenshot(isLocalScreenshot) {}
status_t getResult() const {
return result;
}
virtual bool handler() {
Mutex::Autolock _l(flinger->mStateLock);
- sp<const DisplayDevice> hw(flinger->getDisplayDeviceLocked(display));
- result = flinger->captureScreenImplLocked(hw, producer,
- sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ,
- useIdentityTransform, rotation, isLocalScreenshot);
+ result = flinger->captureScreenImplLocked(*renderArea, traverseLayers, producer,
+ useIdentityTransform, isLocalScreenshot);
static_cast<GraphicProducerWrapper*>(IInterface::asBinder(producer).get())->exit(result);
return true;
}
@@ -3855,9 +3894,8 @@
// the asInterface() call below creates our "fake" BpGraphicBufferProducer
// which does the marshaling work forwards to our "fake remote" above.
sp<MessageBase> msg = new MessageCaptureScreen(this,
- display, IGraphicBufferProducer::asInterface( wrapper ),
- sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ,
- useIdentityTransform, rotationFlags, isLocalScreenshot);
+ &renderArea, traverseLayers, IGraphicBufferProducer::asInterface( wrapper ),
+ useIdentityTransform, isLocalScreenshot);
status_t res = postMessageAsync(msg);
if (res == NO_ERROR) {
@@ -3866,41 +3904,42 @@
return res;
}
-
-void SurfaceFlinger::renderScreenImplLocked(
- const sp<const DisplayDevice>& hw,
- Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- int32_t minLayerZ, int32_t maxLayerZ,
- bool yswap, bool useIdentityTransform, Transform::orientation_flags rotation)
+void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
+ bool yswap, bool useIdentityTransform)
{
ATRACE_CALL();
RenderEngine& engine(getRenderEngine());
// get screen geometry
- const int32_t hw_w = hw->getWidth();
- const int32_t hw_h = hw->getHeight();
- const bool filtering = static_cast<int32_t>(reqWidth) != hw_w ||
- static_cast<int32_t>(reqHeight) != hw_h;
+ const auto raWidth = renderArea.getWidth();
+ const auto raHeight = renderArea.getHeight();
+
+ const auto reqWidth = renderArea.getReqWidth();
+ const auto reqHeight = renderArea.getReqHeight();
+ Rect sourceCrop = renderArea.getSourceCrop();
+
+ const bool filtering = static_cast<int32_t>(reqWidth) != raWidth ||
+ static_cast<int32_t>(reqHeight) != raHeight;
// if a default or invalid sourceCrop is passed in, set reasonable values
if (sourceCrop.width() == 0 || sourceCrop.height() == 0 ||
!sourceCrop.isValid()) {
sourceCrop.setLeftTop(Point(0, 0));
- sourceCrop.setRightBottom(Point(hw_w, hw_h));
+ sourceCrop.setRightBottom(Point(raWidth, raHeight));
}
// ensure that sourceCrop is inside screen
if (sourceCrop.left < 0) {
ALOGE("Invalid crop rect: l = %d (< 0)", sourceCrop.left);
}
- if (sourceCrop.right > hw_w) {
- ALOGE("Invalid crop rect: r = %d (> %d)", sourceCrop.right, hw_w);
+ if (sourceCrop.right > raWidth) {
+ ALOGE("Invalid crop rect: r = %d (> %d)", sourceCrop.right, raWidth);
}
if (sourceCrop.top < 0) {
ALOGE("Invalid crop rect: t = %d (< 0)", sourceCrop.top);
}
- if (sourceCrop.bottom > hw_h) {
- ALOGE("Invalid crop rect: b = %d (> %d)", sourceCrop.bottom, hw_h);
+ if (sourceCrop.bottom > raHeight) {
+ ALOGE("Invalid crop rect: b = %d (> %d)", sourceCrop.bottom, raHeight);
}
// make sure to clear all GL error flags
@@ -3908,77 +3947,35 @@
// set-up our viewport
engine.setViewportAndProjection(
- reqWidth, reqHeight, sourceCrop, hw_h, yswap, rotation);
+ reqWidth, reqHeight, sourceCrop, raHeight, yswap, renderArea.getRotationFlags());
engine.disableTexturing();
// redraw the screen entirely...
engine.clearWithColor(0, 0, 0, 1);
- // We loop through the first level of layers without traversing,
- // as we need to interpret min/max layer Z in the top level Z space.
- for (const auto& layer : mDrawingState.layersSortedByZ) {
- if (layer->getLayerStack() != hw->getLayerStack()) {
- continue;
- }
- const Layer::State& state(layer->getDrawingState());
- if (state.z < minLayerZ || state.z > maxLayerZ) {
- continue;
- }
- layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
- if (!layer->isVisible()) {
- return;
- }
- if (filtering) layer->setFiltering(true);
- layer->draw(hw, useIdentityTransform);
- if (filtering) layer->setFiltering(false);
- });
- }
+ traverseLayers([&](Layer* layer) {
+ if (filtering) layer->setFiltering(true);
+ layer->draw(renderArea, useIdentityTransform);
+ if (filtering) layer->setFiltering(false);
+ });
// compositionComplete is needed for older driver
- hw->compositionComplete();
- hw->setViewportAndProjection();
+// hw->compositionComplete();
+// hw->setViewportAndProjection();
}
-
-status_t SurfaceFlinger::captureScreenImplLocked(
- const sp<const DisplayDevice>& hw,
- const sp<IGraphicBufferProducer>& producer,
- Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- int32_t minLayerZ, int32_t maxLayerZ,
- bool useIdentityTransform, Transform::orientation_flags rotation,
- bool isLocalScreenshot)
-{
+status_t SurfaceFlinger::captureScreenImplLocked(const RenderArea& renderArea,
+ TraverseLayersFunction traverseLayers,
+ const sp<IGraphicBufferProducer>& producer,
+ bool useIdentityTransform,
+ bool isLocalScreenshot) {
ATRACE_CALL();
- // get screen geometry
- uint32_t hw_w = hw->getWidth();
- uint32_t hw_h = hw->getHeight();
-
- if (rotation & Transform::ROT_90) {
- std::swap(hw_w, hw_h);
- }
-
- if ((reqWidth > hw_w) || (reqHeight > hw_h)) {
- ALOGE("size mismatch (%d, %d) > (%d, %d)",
- reqWidth, reqHeight, hw_w, hw_h);
- return BAD_VALUE;
- }
-
- reqWidth = (!reqWidth) ? hw_w : reqWidth;
- reqHeight = (!reqHeight) ? hw_h : reqHeight;
-
bool secureLayerIsVisible = false;
- for (const auto& layer : mDrawingState.layersSortedByZ) {
- const Layer::State& state(layer->getDrawingState());
- if ((layer->getLayerStack() != hw->getLayerStack()) ||
- (state.z < minLayerZ || state.z > maxLayerZ)) {
- continue;
- }
- layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer *layer) {
- secureLayerIsVisible = secureLayerIsVisible || (layer->isVisible() &&
- layer->isSecure());
- });
- }
+ traverseLayers([&](Layer *layer) {
+ secureLayerIsVisible = secureLayerIsVisible || (layer->isVisible() &&
+ layer->isSecure());
+ });
if (!isLocalScreenshot && secureLayerIsVisible) {
ALOGW("FB is protected: PERMISSION_DENIED");
@@ -3996,7 +3993,8 @@
GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
int err = 0;
- err = native_window_set_buffers_dimensions(window, reqWidth, reqHeight);
+ err = native_window_set_buffers_dimensions(window, renderArea.getReqWidth(),
+ renderArea.getReqHeight());
err |= native_window_set_scaling_mode(window, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
err |= native_window_set_buffers_format(window, HAL_PIXEL_FORMAT_RGBA_8888);
err |= native_window_set_usage(window, usage);
@@ -4022,9 +4020,7 @@
// via an FBO, which means we didn't have to create
// an EGLSurface and therefore we're not
// dependent on the context's EGLConfig.
- renderScreenImplLocked(
- hw, sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ, true,
- useIdentityTransform, rotation);
+ renderScreenImplLocked(renderArea, traverseLayers, true, useIdentityTransform);
// Attempt to create a sync khr object that can produce a sync point. If that
// isn't available, create a non-dupable sync object in the fallback path and
@@ -4064,10 +4060,12 @@
}
}
if (DEBUG_SCREENSHOTS) {
- uint32_t* pixels = new uint32_t[reqWidth*reqHeight];
- getRenderEngine().readPixels(0, 0, reqWidth, reqHeight, pixels);
- checkScreenshot(reqWidth, reqHeight, reqWidth, pixels,
- hw, minLayerZ, maxLayerZ);
+ uint32_t* pixels = new uint32_t[renderArea.getReqWidth() *
+ renderArea.getReqHeight()];
+ getRenderEngine().readPixels(0, 0, renderArea.getReqWidth(),
+ renderArea.getReqHeight(), pixels);
+ checkScreenshot(renderArea.getReqWidth(), renderArea.getReqHeight(),
+ renderArea.getReqWidth(), pixels, traverseLayers);
delete [] pixels;
}
@@ -4097,7 +4095,7 @@
}
void SurfaceFlinger::checkScreenshot(size_t w, size_t s, size_t h, void const* vaddr,
- const sp<const DisplayDevice>& hw, int32_t minLayerZ, int32_t maxLayerZ) {
+ TraverseLayersFunction traverseLayers) {
if (DEBUG_SCREENSHOTS) {
for (size_t y=0 ; y<h ; y++) {
uint32_t const * p = (uint32_t const *)vaddr + y*s;
@@ -4105,23 +4103,17 @@
if (p[x] != 0xFF000000) return;
}
}
- ALOGE("*** we just took a black screenshot ***\n"
- "requested minz=%d, maxz=%d, layerStack=%d",
- minLayerZ, maxLayerZ, hw->getLayerStack());
+ ALOGE("*** we just took a black screenshot ***");
+
size_t i = 0;
- for (const auto& layer : mDrawingState.layersSortedByZ) {
+ traverseLayers([&](Layer* layer) {
const Layer::State& state(layer->getDrawingState());
- if (layer->getLayerStack() == hw->getLayerStack() && state.z >= minLayerZ &&
- state.z <= maxLayerZ) {
- layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
- ALOGE("%c index=%zu, name=%s, layerStack=%d, z=%d, visible=%d, flags=%x, alpha=%.3f",
- layer->isVisible() ? '+' : '-',
- i, layer->getName().string(), layer->getLayerStack(), state.z,
- layer->isVisible(), state.flags, static_cast<float>(state.color.a));
- i++;
- });
- }
- }
+ ALOGE("%c index=%zu, name=%s, layerStack=%d, z=%d, visible=%d, flags=%x, alpha=%.3f",
+ layer->isVisible() ? '+' : '-',
+ i, layer->getName().string(), layer->getLayerStack(), state.z,
+ layer->isVisible(), state.flags, static_cast<float>(state.color.a));
+ i++;
+ });
}
}
@@ -4135,6 +4127,28 @@
layersSortedByZ.traverseInReverseZOrder(stateSet, visitor);
}
+void SurfaceFlinger::traverseLayersInDisplay(const sp<const DisplayDevice>& hw,
+ int32_t minLayerZ, int32_t maxLayerZ, const LayerVector::Visitor& visitor) {
+
+ // We loop through the first level of layers without traversing,
+ // as we need to interpret min/max layer Z in the top level Z space.
+ for (const auto& layer : mDrawingState.layersSortedByZ) {
+ if (!layer->belongsToDisplay(hw->getLayerStack(), false)) {
+ continue;
+ }
+ const Layer::State& state(layer->getDrawingState());
+ if (state.z < minLayerZ || state.z > maxLayerZ) {
+ continue;
+ }
+ layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
+ if (!layer->isVisible()) {
+ return;
+ }
+ visitor(layer);
+ });
+ }
+}
+
}; // namespace android
diff --git a/services/surfaceflinger/Transform.cpp b/services/surfaceflinger/Transform.cpp
index 6be9ae2..37925a1 100644
--- a/services/surfaceflinger/Transform.cpp
+++ b/services/surfaceflinger/Transform.cpp
@@ -20,8 +20,8 @@
#include <utils/String8.h>
#include <ui/Region.h>
-#include "clz.h"
#include "Transform.h"
+#include "clz.h"
// ---------------------------------------------------------------------------
@@ -388,6 +388,23 @@
ALOGD("%.4f %.4f %.4f", m[0][2], m[1][2], m[2][2]);
}
+Transform::orientation_flags Transform::fromRotation(ISurfaceComposer::Rotation rotation) {
+ // Convert to surfaceflinger's internal rotation type.
+ switch (rotation) {
+ case ISurfaceComposer::eRotateNone:
+ return Transform::ROT_0;
+ case ISurfaceComposer::eRotate90:
+ return Transform::ROT_90;
+ case ISurfaceComposer::eRotate180:
+ return Transform::ROT_180;
+ case ISurfaceComposer::eRotate270:
+ return Transform::ROT_270;
+ default:
+ ALOGE("Invalid rotation passed to captureScreen(): %d\n", rotation);
+ return Transform::ROT_0;
+ }
+}
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/services/surfaceflinger/Transform.h b/services/surfaceflinger/Transform.h
index 6640a13..bfc66ec 100644
--- a/services/surfaceflinger/Transform.h
+++ b/services/surfaceflinger/Transform.h
@@ -25,6 +25,8 @@
#include <math/vec2.h>
#include <math/vec3.h>
+#include <gui/ISurfaceComposer.h>
+
#include <hardware/hardware.h>
namespace android {
@@ -51,6 +53,8 @@
ROT_INVALID = 0x80
};
+ static orientation_flags fromRotation(ISurfaceComposer::Rotation rotation);
+
enum type_mask {
IDENTITY = 0,
TRANSLATE = 0x1,
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index f61a978..ec87eee 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -116,6 +116,60 @@
CpuConsumer::LockedBuffer mBuf;
};
+class CaptureLayer {
+public:
+ static void captureScreen(std::unique_ptr<CaptureLayer>* sc, sp<IBinder>& parentHandle) {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+ sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ sp<IBinder> display(sf->getBuiltInDisplay(
+ ISurfaceComposer::eDisplayIdMain));
+ SurfaceComposerClient::Transaction().apply(true);
+ ASSERT_EQ(NO_ERROR, sf->captureLayers(parentHandle, producer));
+ *sc = std::make_unique<CaptureLayer>(cpuConsumer);
+ }
+
+ void checkPixel(uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b) {
+ ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mBuffer.format);
+ const uint8_t* img = static_cast<const uint8_t*>(mBuffer.data);
+ const uint8_t* pixel = img + (4 * (y * mBuffer.stride + x));
+ if (r != pixel[0] || g != pixel[1] || b != pixel[2]) {
+ String8 err(String8::format("pixel @ (%3d, %3d): "
+ "expected [%3d, %3d, %3d], got [%3d, %3d, %3d]",
+ x, y, r, g, b, pixel[0], pixel[1], pixel[2]));
+ EXPECT_EQ(String8(), err) << err.string();
+ }
+ }
+
+ void expectFGColor(uint32_t x, uint32_t y) {
+ checkPixel(x, y, 195, 63, 63);
+ }
+
+ void expectBGColor(uint32_t x, uint32_t y) {
+ checkPixel(x, y, 63, 63, 195);
+ }
+
+ void expectChildColor(uint32_t x, uint32_t y) {
+ checkPixel(x, y, 200, 200, 200);
+ }
+
+ CaptureLayer(const sp<CpuConsumer>& cc) :
+ mCC(cc) {
+ EXPECT_EQ(NO_ERROR, mCC->lockNextBuffer(&mBuffer));
+ }
+
+ ~CaptureLayer() {
+ mCC->unlockBuffer(mBuffer);
+ }
+
+private:
+ sp<CpuConsumer> mCC;
+ CpuConsumer::LockedBuffer mBuffer;
+};
+
+
class LayerUpdateTest : public ::testing::Test {
protected:
virtual void SetUp() {
@@ -1431,4 +1485,109 @@
}
}
+class ScreenCaptureTest : public LayerUpdateTest {
+protected:
+ std::unique_ptr<CaptureLayer> mCapture;
+};
+
+TEST_F(ScreenCaptureTest, CaptureSingleLayer) {
+ auto bgHandle = mBGSurfaceControl->getHandle();
+ CaptureLayer::captureScreen(&mCapture, bgHandle);
+ mCapture->expectBGColor(0, 0);
+ // Doesn't capture FG layer which is at 64, 64
+ mCapture->expectBGColor(64, 64);
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerWithChild) {
+ auto fgHandle = mFGSurfaceControl->getHandle();
+
+ sp<SurfaceControl> child = mComposerClient->createSurface(
+ String8("Child surface"),
+ 10, 10, PIXEL_FORMAT_RGBA_8888,
+ 0, mFGSurfaceControl.get());
+ fillSurfaceRGBA8(child, 200, 200, 200);
+
+ SurfaceComposerClient::Transaction()
+ .show(child)
+ .apply(true);
+
+ // Captures mFGSurfaceControl layer and its child.
+ CaptureLayer::captureScreen(&mCapture, fgHandle);
+ mCapture->expectFGColor(10, 10);
+ mCapture->expectChildColor(0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerWithGrandchild) {
+ auto fgHandle = mFGSurfaceControl->getHandle();
+
+ sp<SurfaceControl> child = mComposerClient->createSurface(
+ String8("Child surface"),
+ 10, 10, PIXEL_FORMAT_RGBA_8888,
+ 0, mFGSurfaceControl.get());
+ fillSurfaceRGBA8(child, 200, 200, 200);
+
+ sp<SurfaceControl> grandchild = mComposerClient->createSurface(
+ String8("Grandchild surface"), 5, 5,
+ PIXEL_FORMAT_RGBA_8888, 0, child.get());
+
+ fillSurfaceRGBA8(grandchild, 50, 50, 50);
+ SurfaceComposerClient::Transaction()
+ .show(child)
+ .setPosition(grandchild, 5, 5)
+ .show(grandchild)
+ .apply(true);
+
+ // Captures mFGSurfaceControl, its child, and the grandchild.
+ CaptureLayer::captureScreen(&mCapture, fgHandle);
+ mCapture->expectFGColor(10, 10);
+ mCapture->expectChildColor(0, 0);
+ mCapture->checkPixel(5, 5, 50, 50, 50);
+}
+
+TEST_F(ScreenCaptureTest, CaptureChildOnly) {
+ sp<SurfaceControl> child = mComposerClient->createSurface(
+ String8("Child surface"),
+ 10, 10, PIXEL_FORMAT_RGBA_8888,
+ 0, mFGSurfaceControl.get());
+ fillSurfaceRGBA8(child, 200, 200, 200);
+ auto childHandle = child->getHandle();
+
+ SurfaceComposerClient::Transaction()
+ .setPosition(child, 5, 5)
+ .show(child)
+ .apply(true);
+
+ // Captures only the child layer, and not the parent.
+ CaptureLayer::captureScreen(&mCapture, childHandle);
+ mCapture->expectChildColor(0, 0);
+ mCapture->expectChildColor(9, 9);
+}
+
+TEST_F(ScreenCaptureTest, CaptureGrandchildOnly) {
+ sp<SurfaceControl> child = mComposerClient->createSurface(
+ String8("Child surface"),
+ 10, 10, PIXEL_FORMAT_RGBA_8888,
+ 0, mFGSurfaceControl.get());
+ fillSurfaceRGBA8(child, 200, 200, 200);
+ auto childHandle = child->getHandle();
+
+ sp<SurfaceControl> grandchild = mComposerClient->createSurface(
+ String8("Grandchild surface"), 5, 5,
+ PIXEL_FORMAT_RGBA_8888, 0, child.get());
+ fillSurfaceRGBA8(grandchild, 50, 50, 50);
+
+ SurfaceComposerClient::Transaction()
+ .show(child)
+ .setPosition(grandchild, 5, 5)
+ .show(grandchild)
+ .apply(true);
+
+ auto grandchildHandle = grandchild->getHandle();
+
+ // Captures only the grandchild.
+ CaptureLayer::captureScreen(&mCapture, grandchildHandle);
+ mCapture->checkPixel(0, 0, 50, 50, 50);
+ mCapture->checkPixel(4, 4, 50, 50, 50);
+}
+
}