From fb796315ed0f3645575995a7f1ab4ece8e20363a Mon Sep 17 00:00:00 2001 From: chaviw Date: Wed, 28 Apr 2021 15:33:21 -0500 Subject: [PATCH] Add CAPTURE_BLACKOUT_CONTENT permission Added a permssion to allow an application to capture screenshots of layers that would normally be blacked out. Test: Builds, screenshots still work as before Test: ScreenshotTests Bug: 173746627 Change-Id: I27237c8e340904cbaa82c7e5793de35cab85d8de --- core/res/AndroidManifest.xml | 10 + libs/WindowManager/Shell/AndroidManifest.xml | 1 + services/tests/wmtests/AndroidManifest.xml | 2 + .../android/server/wm/ScreenshotTests.java | 234 ++++++++++++++++++ 4 files changed, 247 insertions(+) create mode 100644 services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index ffb8d1daac906..78184dab1c700 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -5768,6 +5768,16 @@ android:protectionLevel="normal" /> + + + diff --git a/libs/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml index 6bd0e0a4ff860..10df726a8c908 100644 --- a/libs/WindowManager/Shell/AndroidManifest.xml +++ b/libs/WindowManager/Shell/AndroidManifest.xml @@ -18,6 +18,7 @@ + diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml index de4698dcf3b25..985b2d5f24baa 100644 --- a/services/tests/wmtests/AndroidManifest.xml +++ b/services/tests/wmtests/AndroidManifest.xml @@ -41,6 +41,7 @@ + + mActivityRule = + new ActivityTestRule<>(ScreenshotActivity.class); + + private ScreenshotActivity mActivity; + + @Before + public void setup() { + mActivity = mActivityRule.getActivity(); + mInstrumentation.waitForIdleSync(); + } + + @Test + public void testScreenshotSecureLayers() { + SurfaceControl secureSC = new SurfaceControl.Builder() + .setName("SecureChildSurfaceControl") + .setBLASTLayer() + .setCallsite("makeSecureSurfaceControl") + .setSecure(true) + .build(); + + SurfaceControl.Transaction t = mActivity.addChildSc(secureSC); + mInstrumentation.waitForIdleSync(); + + GraphicBuffer buffer = GraphicBuffer.create(BUFFER_WIDTH, BUFFER_HEIGHT, + PixelFormat.RGBA_8888, + GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_HW_COMPOSER + | GraphicBuffer.USAGE_SW_WRITE_RARELY); + + Canvas canvas = buffer.lockCanvas(); + canvas.drawColor(Color.RED); + buffer.unlockCanvasAndPost(canvas); + + t.show(secureSC) + .setBuffer(secureSC, buffer) + .setColorSpace(secureSC, ColorSpace.get(ColorSpace.Named.SRGB)) + .apply(true); + + SurfaceControl.LayerCaptureArgs args = new SurfaceControl.LayerCaptureArgs.Builder(secureSC) + .setCaptureSecureLayers(true) + .setChildrenOnly(false) + .build(); + SurfaceControl.ScreenshotHardwareBuffer hardwareBuffer = SurfaceControl.captureLayers(args); + assertNotNull(hardwareBuffer); + + Bitmap screenshot = hardwareBuffer.asBitmap(); + assertNotNull(screenshot); + + Bitmap swBitmap = screenshot.copy(Bitmap.Config.ARGB_8888, false); + screenshot.recycle(); + + int numMatchingPixels = PixelChecker.getNumMatchingPixels(swBitmap, + new PixelColor(PixelColor.RED)); + long sizeOfBitmap = swBitmap.getWidth() * swBitmap.getHeight(); + boolean success = numMatchingPixels == sizeOfBitmap; + swBitmap.recycle(); + + assertTrue(success); + } + + public static class ScreenshotActivity extends Activity { + private static final long WAIT_TIMEOUT_S = 5; + private final Handler mHandler = new Handler(Looper.getMainLooper()); + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().getDecorView().setPointerIcon( + PointerIcon.getSystemIcon(this, PointerIcon.TYPE_NULL)); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + + SurfaceControl.Transaction addChildSc(SurfaceControl surfaceControl) { + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + CountDownLatch countDownLatch = new CountDownLatch(1); + mHandler.post(() -> { + t.merge(getWindow().getRootSurfaceControl().buildReparentTransaction( + surfaceControl)); + countDownLatch.countDown(); + }); + + try { + countDownLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS); + } catch (InterruptedException e) { + } + return t; + } + } + + public abstract static class PixelChecker { + static int getNumMatchingPixels(Bitmap bitmap, PixelColor pixelColor) { + int numMatchingPixels = 0; + for (int x = 0; x < bitmap.getWidth(); x++) { + for (int y = 0; y < bitmap.getHeight(); y++) { + int color = bitmap.getPixel(x, y); + if (matchesColor(pixelColor, color)) { + numMatchingPixels++; + } + } + } + return numMatchingPixels; + } + + static boolean matchesColor(PixelColor expectedColor, int color) { + final float red = Color.red(color); + final float green = Color.green(color); + final float blue = Color.blue(color); + final float alpha = Color.alpha(color); + + return alpha <= expectedColor.mMaxAlpha + && alpha >= expectedColor.mMinAlpha + && red <= expectedColor.mMaxRed + && red >= expectedColor.mMinRed + && green <= expectedColor.mMaxGreen + && green >= expectedColor.mMinGreen + && blue <= expectedColor.mMaxBlue + && blue >= expectedColor.mMinBlue; + } + } + + public static class PixelColor { + public static final int BLACK = 0xFF000000; + public static final int RED = 0xFF0000FF; + public static final int GREEN = 0xFF00FF00; + public static final int BLUE = 0xFFFF0000; + public static final int YELLOW = 0xFF00FFFF; + public static final int MAGENTA = 0xFFFF00FF; + public static final int WHITE = 0xFFFFFFFF; + + public static final int TRANSPARENT_RED = 0x7F0000FF; + public static final int TRANSPARENT_BLUE = 0x7FFF0000; + public static final int TRANSPARENT = 0x00000000; + + // Default to black + public short mMinAlpha; + public short mMaxAlpha; + public short mMinRed; + public short mMaxRed; + public short mMinBlue; + public short mMaxBlue; + public short mMinGreen; + public short mMaxGreen; + + public PixelColor(int color) { + short alpha = (short) ((color >> 24) & 0xFF); + short blue = (short) ((color >> 16) & 0xFF); + short green = (short) ((color >> 8) & 0xFF); + short red = (short) (color & 0xFF); + + mMinAlpha = (short) getMinValue(alpha); + mMaxAlpha = (short) getMaxValue(alpha); + mMinRed = (short) getMinValue(red); + mMaxRed = (short) getMaxValue(red); + mMinBlue = (short) getMinValue(blue); + mMaxBlue = (short) getMaxValue(blue); + mMinGreen = (short) getMinValue(green); + mMaxGreen = (short) getMaxValue(green); + } + + public PixelColor() { + this(BLACK); + } + + private int getMinValue(short color) { + return Math.max(color - 4, 0); + } + + private int getMaxValue(short color) { + return Math.min(color + 4, 0xFF); + } + } +}