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

Commit 4ddd5438 authored by jeimysantiago's avatar jeimysantiago
Browse files

Fix screenshot buffer leaks

By passing a listener into UiAutomationConnection the client can get ownership and free the buffer on its own.

Bug: 287081404
Test: atest displayScreenshot
Test: atest  layerScreenshot
Change-Id: I15824755c4691d9ff7be4a6da70cb0f713f5541d
parent fcb72565
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -24,6 +24,8 @@ import android.view.SurfaceControl;
import android.view.WindowContentFrameStats;
import android.view.WindowAnimationFrameStats;
import android.os.ParcelFileDescriptor;
import android.window.ScreenCapture.ScreenCaptureListener;
import android.window.ScreenCapture.LayerCaptureArgs;

import java.util.List;

@@ -43,8 +45,8 @@ interface IUiAutomationConnection {
    void injectInputEventToInputFilter(in InputEvent event);
    void syncInputTransactions(boolean waitForAnimations);
    boolean setRotation(int rotation);
    Bitmap takeScreenshot(in Rect crop);
    Bitmap takeSurfaceControlScreenshot(in SurfaceControl surfaceControl);
    boolean takeScreenshot(in Rect crop, in ScreenCaptureListener listener);
    boolean takeSurfaceControlScreenshot(in SurfaceControl surfaceControl, in ScreenCaptureListener listener);
    boolean clearWindowContentFrameStats(int windowId);
    WindowContentFrameStats getWindowContentFrameStats(int windowId);
    void clearWindowAnimationFrameStats();
+39 −13
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.HardwareBuffer;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Build;
import android.os.Handler;
@@ -71,6 +72,8 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.inputmethod.EditorInfo;
import android.window.ScreenCapture;
import android.window.ScreenCapture.ScreenshotHardwareBuffer;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -1160,17 +1163,12 @@ public final class UiAutomation {
        Point displaySize = new Point();
        display.getRealSize(displaySize);

        int rotation = display.getRotation();

        // Take the screenshot
        Bitmap screenShot = null;
        ScreenCapture.SynchronousScreenCaptureListener syncScreenCapture =
                ScreenCapture.createSyncCaptureListener();
        try {
            // Calling out without a lock held.
            screenShot = mUiAutomationConnection.takeScreenshot(
                    new Rect(0, 0, displaySize.x, displaySize.y));
            if (screenShot == null) {
                Log.e(LOG_TAG, "mUiAutomationConnection.takeScreenshot() returned null for display "
                        + mDisplayId);
            if (!mUiAutomationConnection.takeScreenshot(
                    new Rect(0, 0, displaySize.x, displaySize.y), syncScreenCapture)) {
                return null;
            }
        } catch (RemoteException re) {
@@ -1178,10 +1176,23 @@ public final class UiAutomation {
            return null;
        }

        // Optimization
        screenShot.setHasAlpha(false);
        final ScreenshotHardwareBuffer screenshotBuffer =
                syncScreenCapture.getBuffer();
        Bitmap screenShot = screenshotBuffer.asBitmap();
        if (screenShot == null) {
            Log.e(LOG_TAG, "mUiAutomationConnection.takeScreenshot() returned null for display "
                    + mDisplayId);
            return null;
        }
        Bitmap swBitmap;
        try (HardwareBuffer buffer = screenshotBuffer.getHardwareBuffer()) {
            swBitmap = screenShot.copy(Bitmap.Config.ARGB_8888, false);
        }
        screenShot.recycle();

        return screenShot;
        // Optimization
        swBitmap.setHasAlpha(false);
        return swBitmap;
    }

    /**
@@ -1218,12 +1229,27 @@ public final class UiAutomation {
        // Apply a sync transaction to ensure SurfaceFlinger is flushed before capturing a
        // screenshot.
        new SurfaceControl.Transaction().apply(true);
        ScreenCapture.SynchronousScreenCaptureListener syncScreenCapture =
                ScreenCapture.createSyncCaptureListener();
        try {
            return mUiAutomationConnection.takeSurfaceControlScreenshot(sc);
            if (!mUiAutomationConnection.takeSurfaceControlScreenshot(sc, syncScreenCapture)) {
                return null;
            }

        } catch (RemoteException re) {
            Log.e(LOG_TAG, "Error while taking screenshot!", re);
            return null;
        }
        ScreenCapture.ScreenshotHardwareBuffer captureBuffer =
                syncScreenCapture.getBuffer();
        Bitmap screenShot = captureBuffer.asBitmap();
        Bitmap swBitmap;
        try (HardwareBuffer buffer = captureBuffer.getHardwareBuffer()) {
            swBitmap = screenShot.copy(Bitmap.Config.ARGB_8888, false);
        }

        screenShot.recycle();
        return swBitmap;
    }

    /**
+16 −21
Original line number Diff line number Diff line
@@ -25,7 +25,6 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.hardware.input.InputManager;
import android.hardware.input.InputManagerGlobal;
@@ -51,8 +50,6 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.IAccessibilityManager;
import android.window.ScreenCapture;
import android.window.ScreenCapture.CaptureArgs;
import android.window.ScreenCapture.ScreenshotHardwareBuffer;
import android.window.ScreenCapture.SynchronousScreenCaptureListener;

import libcore.io.IoUtils;

@@ -224,56 +221,54 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
    }

    @Override
    public Bitmap takeScreenshot(Rect crop) {
    public boolean takeScreenshot(Rect crop, ScreenCapture.ScreenCaptureListener listener) {
        synchronized (mLock) {
            throwIfCalledByNotTrustedUidLocked();
            throwIfShutdownLocked();
            throwIfNotConnectedLocked();
        }

        final long identity = Binder.clearCallingIdentity();
        try {
            final CaptureArgs captureArgs = new CaptureArgs.Builder<>()
                    .setSourceCrop(crop)
                    .build();
            SynchronousScreenCaptureListener syncScreenCapture =
                    ScreenCapture.createSyncCaptureListener();
            mWindowManager.captureDisplay(DEFAULT_DISPLAY, captureArgs,
                    syncScreenCapture);
            final ScreenshotHardwareBuffer screenshotBuffer =
                    syncScreenCapture.getBuffer();
            return screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
            mWindowManager.captureDisplay(DEFAULT_DISPLAY, captureArgs, listener);
        } catch (RemoteException re) {
            re.rethrowAsRuntimeException();
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
        return null;

        return true;
    }

    @Nullable
    @Override
    public Bitmap takeSurfaceControlScreenshot(@NonNull SurfaceControl surfaceControl) {
    public boolean takeSurfaceControlScreenshot(@NonNull SurfaceControl surfaceControl,
            ScreenCapture.ScreenCaptureListener listener) {
        synchronized (mLock) {
            throwIfCalledByNotTrustedUidLocked();
            throwIfShutdownLocked();
            throwIfNotConnectedLocked();
        }

        ScreenCapture.ScreenshotHardwareBuffer captureBuffer;
        final long identity = Binder.clearCallingIdentity();
        try {
            captureBuffer = ScreenCapture.captureLayers(
            ScreenCapture.LayerCaptureArgs args =
                    new ScreenCapture.LayerCaptureArgs.Builder(surfaceControl)
                    .setChildrenOnly(false)
                            .build());
                    .build();
            int status = ScreenCapture.captureLayers(args, listener);

            if (status != 0) {
                return false;
            }
        } finally {
            Binder.restoreCallingIdentity(identity);
        }

        if (captureBuffer == null) {
            return null;
        }
        return captureBuffer.asBitmap();
        return true;
    }

    @Override