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

Commit 6a7a8599 authored by Jorim Jaggi's avatar Jorim Jaggi
Browse files

Use screenshotToBuffer

To avoid copying buffers. Yay!

Test: Open app, go to recents, make sure screenshot is showing.
Bug: 31339431
Change-Id: I62736b8ba9ca45155d602286de8280304160bbd6
parent 02d091aa
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -617,7 +617,7 @@ public class AppWindowContainerController
            }
            return dc.screenshotApplications(mToken.asBinder(), width, height,
                    false /* includeFullDisplay */, frameScale, Bitmap.Config.RGB_565,
                    false /* wallpaperOnly */, false /* includeDecor */, true /* toAshmem */);
                    false /* wallpaperOnly */, false /* includeDecor */);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
        }
+60 −40
Original line number Diff line number Diff line
@@ -94,6 +94,7 @@ import android.annotation.NonNull;
import android.app.ActivityManager.StackId;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.GraphicBuffer;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -2105,12 +2106,55 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
     * @param wallpaperOnly true if only the wallpaper layer should be included in the screenshot
     * @param includeDecor whether to include window decors, like the status or navigation bar
     *                     background of the window
     * @param toAshmem whether to convert the resulting bitmap to ashmem; this should be set to
     *                 true if the Bitmap is sent over binder, and false otherwise
     */
    Bitmap screenshotApplications(IBinder appToken, int width, int height,
            boolean includeFullDisplay, float frameScale, Bitmap.Config config,
            boolean wallpaperOnly, boolean includeDecor, boolean toAshmem) {
            boolean wallpaperOnly, boolean includeDecor) {
        Bitmap bitmap = screenshotApplications(appToken, width, height, includeFullDisplay,
                frameScale, wallpaperOnly, includeDecor, SurfaceControl::screenshot);

        if (DEBUG_SCREENSHOT) {
            // TEST IF IT's ALL BLACK
            int[] buffer = new int[bitmap.getWidth() * bitmap.getHeight()];
            bitmap.getPixels(buffer, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(),
                    bitmap.getHeight());
            boolean allBlack = true;
            final int firstColor = buffer[0];
            for (int i = 0; i < buffer.length; i++) {
                if (buffer[i] != firstColor) {
                    allBlack = false;
                    break;
                }
            }
            if (allBlack) {
                final WindowState appWin = mScreenshotApplicationState.appWin;
                final int maxLayer = mScreenshotApplicationState.maxLayer;
                final int minLayer = mScreenshotApplicationState.minLayer;
                Slog.i(TAG_WM, "Screenshot " + appWin + " was monochrome(" +
                        Integer.toHexString(firstColor) + ")! mSurfaceLayer=" +
                        (appWin != null ?
                                appWin.mWinAnimator.mSurfaceController.getLayer() : "null") +
                        " minLayer=" + minLayer + " maxLayer=" + maxLayer);
            }
        }

        // Create a copy of the screenshot that is immutable and backed in ashmem.
        // This greatly reduces the overhead of passing the bitmap between processes.
        Bitmap ret = bitmap.createAshmemBitmap(config);
        bitmap.recycle();
        return ret;
    }

    GraphicBuffer screenshotApplicationsToBuffer(IBinder appToken, int width, int height,
            boolean includeFullDisplay, float frameScale, boolean wallpaperOnly,
            boolean includeDecor) {
        return screenshotApplications(appToken, width, height, includeFullDisplay, frameScale,
                wallpaperOnly, includeDecor, SurfaceControl::screenshotToBuffer);
    }

    private <E> E screenshotApplications(IBinder appToken, int width, int height,
            boolean includeFullDisplay, float frameScale, boolean wallpaperOnly,
            boolean includeDecor, Screenshoter<E> screenshoter) {
        int dw = mDisplayInfo.logicalWidth;
        int dh = mDisplayInfo.logicalHeight;
        if (dw == 0 || dh == 0) {
@@ -2119,7 +2163,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
            return null;
        }

        Bitmap bm = null;
        E bitmap;

        mScreenshotApplicationState.reset(appToken == null && !wallpaperOnly);
        final Rect frame = new Rect();
@@ -2327,48 +2371,15 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
            SurfaceControl.openTransaction();
            SurfaceControl.closeTransactionSync();

            bm = SurfaceControl.screenshot(crop, width, height, minLayer, maxLayer,
            bitmap = screenshoter.screenshot(crop, width, height, minLayer, maxLayer,
                    inRotation, rot);
            if (bm == null) {
            if (bitmap == null) {
                Slog.w(TAG_WM, "Screenshot failure taking screenshot for (" + dw + "x" + dh
                        + ") to layer " + maxLayer);
                return null;
            }
        }

        if (DEBUG_SCREENSHOT) {
            // TEST IF IT's ALL BLACK
            int[] buffer = new int[bm.getWidth() * bm.getHeight()];
            bm.getPixels(buffer, 0, bm.getWidth(), 0, 0, bm.getWidth(), bm.getHeight());
            boolean allBlack = true;
            final int firstColor = buffer[0];
            for (int i = 0; i < buffer.length; i++) {
                if (buffer[i] != firstColor) {
                    allBlack = false;
                    break;
                }
            }
            if (allBlack) {
                final WindowState appWin = mScreenshotApplicationState.appWin;
                final int maxLayer = mScreenshotApplicationState.maxLayer;
                final int minLayer = mScreenshotApplicationState.minLayer;
                Slog.i(TAG_WM, "Screenshot " + appWin + " was monochrome(" +
                        Integer.toHexString(firstColor) + ")! mSurfaceLayer=" +
                        (appWin != null ?
                                appWin.mWinAnimator.mSurfaceController.getLayer() : "null") +
                        " minLayer=" + minLayer + " maxLayer=" + maxLayer);
            }
        }

        // Create a copy of the screenshot that is immutable and backed in ashmem.
        // This greatly reduces the overhead of passing the bitmap between processes.
        if (toAshmem) {
            Bitmap ret = bm.createAshmemBitmap(config);
            bm.recycle();
            return ret;
        } else {
            return bm;
        }
        return bitmap;
    }

    // TODO: Can this use createRotationMatrix()?
@@ -2721,4 +2732,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
            return mName;
        }
    }

    /**
     * Interface to screenshot into various types, i.e. {@link Bitmap} and {@link GraphicBuffer}.
     */
    @FunctionalInterface
    private interface Screenshoter<E> {
        E screenshot(Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
                boolean useIdentityTransform, int rotation);
    }
}
+3 −9
Original line number Diff line number Diff line
@@ -96,17 +96,11 @@ class TaskSnapshotController {
        if (top == null) {
            return null;
        }
        final Bitmap bmp = top.mDisplayContent.screenshotApplications(top.token, -1, -1, false,
                1.0f, ARGB_8888, false, true, false);
        if (bmp == null) {
        final GraphicBuffer buffer = top.mDisplayContent.screenshotApplicationsToBuffer(top.token,
                -1, -1, false, 1.0f, false, true);
        if (buffer == null) {
            return null;
        }
        // TODO: Already use a GraphicBuffer when snapshotting the content.
        final GraphicBuffer buffer = GraphicBuffer.create(bmp.getWidth(), bmp.getHeight(),
                RGBA_8888, USAGE_HW_TEXTURE | USAGE_SW_WRITE_NEVER | USAGE_SW_READ_NEVER);
        final Canvas c = buffer.lockCanvas();
        c.drawBitmap(bmp, 0, 0, null);
        buffer.unlockCanvasAndPost(c);
        return new TaskSnapshot(buffer, top.getConfiguration().orientation,
                top.findMainWindow().mStableInsets);
    }
+4 −7
Original line number Diff line number Diff line
@@ -3862,8 +3862,7 @@ public class WindowManagerService extends IWindowManager.Stub
            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenshotWallpaper");
            return screenshotApplications(null /* appToken */, DEFAULT_DISPLAY, -1 /* width */,
                    -1 /* height */, true /* includeFullDisplay */, 1f /* frameScale */,
                    Bitmap.Config.ARGB_8888, true /* wallpaperOnly */, false /* includeDecor */,
                    true /* toAshmem */);
                    Bitmap.Config.ARGB_8888, true /* wallpaperOnly */, false /* includeDecor */);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
        }
@@ -3885,7 +3884,7 @@ public class WindowManagerService extends IWindowManager.Stub
            Bitmap bm = screenshotApplications(null /* appToken */, DEFAULT_DISPLAY,
                    -1 /* width */, -1 /* height */, true /* includeFullDisplay */,
                    1f /* frameScale */, Bitmap.Config.ARGB_8888, false /* wallpaperOnly */,
                    false /* includeDecor */, true /* toAshmem */);
                    false /* includeDecor */);
            try {
                receiver.send(bm);
            } catch (RemoteException e) {
@@ -3908,12 +3907,10 @@ public class WindowManagerService extends IWindowManager.Stub
     * @param wallpaperOnly true if only the wallpaper layer should be included in the screenshot
     * @param includeDecor whether to include window decors, like the status or navigation bar
     *                     background of the window
     * @param toAshmem whether to convert the resulting bitmap to ashmem; this should be set to
     *                 true if the Bitmap is sent over binder, and false otherwise
     */
    private Bitmap screenshotApplications(IBinder appToken, int displayId, int width,
            int height, boolean includeFullDisplay, float frameScale, Bitmap.Config config,
            boolean wallpaperOnly, boolean includeDecor, boolean toAshmem) {
            boolean wallpaperOnly, boolean includeDecor) {
        final DisplayContent displayContent;
        synchronized(mWindowMap) {
            displayContent = mRoot.getDisplayContentOrCreate(displayId);
@@ -3924,7 +3921,7 @@ public class WindowManagerService extends IWindowManager.Stub
            }
        }
        return displayContent.screenshotApplications(appToken, width, height,
                includeFullDisplay, frameScale, config, wallpaperOnly, includeDecor, toAshmem);
                includeFullDisplay, frameScale, config, wallpaperOnly, includeDecor);
    }

    /**