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

Commit b0938953 authored by Chavi Weingarten's avatar Chavi Weingarten Committed by Android (Google) Code Review
Browse files

Merge "Add captureDisplay API in WMS."

parents 545fa2aa ddd124e8
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@ import android.view.SurfaceControl;
import android.view.displayhash.DisplayHash;
import android.view.displayhash.VerifiedDisplayHash;
import android.window.ITaskFpsCallback;
import android.window.ScreenCapture;

/**
 * System private interface to the window manager.
@@ -968,4 +969,11 @@ interface IWindowManager
     * treatment.
     */
    boolean isLetterboxBackgroundMultiColored();

    /**
     * Captures the entire display specified by the displayId using the args provided. If the args
     * are null or if the sourceCrop is invalid or null, the entire display bounds will be captured.
     */
    oneway void captureDisplay(int displayId, in @nullable ScreenCapture.CaptureArgs captureArgs,
            in ScreenCapture.ScreenCaptureListener listener);
}
+26 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.
 */

package android.window;

/** @hide */
parcelable ScreenCapture.CaptureArgs;

/** @hide */
parcelable ScreenCapture.ScreenshotHardwareBuffer;

/** @hide */
parcelable ScreenCapture.ScreenCaptureListener;
 No newline at end of file
+241 −94
Original line number Diff line number Diff line
@@ -24,11 +24,17 @@ import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
import android.util.Pair;
import android.view.SurfaceControl;

import libcore.util.NativeAllocationRegistry;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

/**
 * Handles display and layer captures for the system.
@@ -37,11 +43,17 @@ import java.util.concurrent.TimeUnit;
 */
public class ScreenCapture {
    private static final String TAG = "ScreenCapture";
    private static final int SCREENSHOT_WAIT_TIME_S = 1;

    private static native int nativeCaptureDisplay(DisplayCaptureArgs captureArgs,
            ScreenCaptureListener captureListener);
            long captureListener);
    private static native int nativeCaptureLayers(LayerCaptureArgs captureArgs,
            ScreenCaptureListener captureListener);
            long captureListener);
    private static native long nativeCreateScreenCaptureListener(
            Consumer<ScreenshotHardwareBuffer> consumer);
    private static native void nativeWriteListenerToParcel(long nativeObject, Parcel out);
    private static native long nativeReadListenerFromParcel(Parcel in);
    private static native long getNativeListenerFinalizer();

    /**
     * @param captureArgs     Arguments about how to take the screenshot
@@ -50,7 +62,7 @@ public class ScreenCapture {
     */
    public static int captureDisplay(@NonNull DisplayCaptureArgs captureArgs,
            @NonNull ScreenCaptureListener captureListener) {
        return nativeCaptureDisplay(captureArgs, captureListener);
        return nativeCaptureDisplay(captureArgs, captureListener.mNativeObject);
    }

    /**
@@ -61,15 +73,17 @@ public class ScreenCapture {
     */
    public static ScreenshotHardwareBuffer captureDisplay(
            DisplayCaptureArgs captureArgs) {
        SyncScreenCaptureListener
                screenCaptureListener = new SyncScreenCaptureListener();

        int status = captureDisplay(captureArgs, screenCaptureListener);
        Pair<ScreenCaptureListener, ScreenshotSync> syncScreenCapture = createSyncCaptureListener();
        int status = captureDisplay(captureArgs, syncScreenCapture.first);
        if (status != 0) {
            return null;
        }

        return screenCaptureListener.waitForScreenshot();
        try {
            return syncScreenCapture.second.get();
        } catch (Exception e) {
            return null;
        }
    }

    /**
@@ -80,9 +94,8 @@ public class ScreenCapture {
     *                   Rect()' or null if no cropping is desired. If the root layer does not
     *                   have a buffer or a crop set, then a non-empty source crop must be
     *                   specified.
     * @param frameScale       The desired scale of the returned buffer; the raw
     *                         screen will be scaled up/down.
     *
     * @param frameScale The desired scale of the returned buffer; the raw screen will be scaled
     *                   up/down.
     * @return Returns a HardwareBuffer that contains the layer capture.
     * @hide
     */
@@ -99,10 +112,9 @@ public class ScreenCapture {
     *                   Rect()' or null if no cropping is desired. If the root layer does not
     *                   have a buffer or a crop set, then a non-empty source crop must be
     *                   specified.
     * @param frameScale       The desired scale of the returned buffer; the raw
     *                         screen will be scaled up/down.
     * @param frameScale The desired scale of the returned buffer; the raw screen will be scaled
     *                   up/down.
     * @param format     The desired pixel format of the returned buffer.
     *
     * @return Returns a HardwareBuffer that contains the layer capture.
     * @hide
     */
@@ -120,21 +132,24 @@ public class ScreenCapture {
    /**
     * @hide
     */
    public static ScreenshotHardwareBuffer captureLayers(
            LayerCaptureArgs captureArgs) {
        SyncScreenCaptureListener screenCaptureListener = new SyncScreenCaptureListener();

        int status = captureLayers(captureArgs, screenCaptureListener);
    public static ScreenshotHardwareBuffer captureLayers(LayerCaptureArgs captureArgs) {
        Pair<ScreenCaptureListener, ScreenshotSync> syncScreenCapture = createSyncCaptureListener();
        int status = captureLayers(captureArgs, syncScreenCapture.first);
        if (status != 0) {
            return null;
        }

        return screenCaptureListener.waitForScreenshot();
        try {
            return syncScreenCapture.second.get();
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Like {@link #captureLayers(SurfaceControl, Rect, float, int)} but with an array of layer
     * handles to exclude.
     *
     * @hide
     */
    public static ScreenshotHardwareBuffer captureLayersExcluding(SurfaceControl layer,
@@ -156,18 +171,7 @@ public class ScreenCapture {
     */
    public static int captureLayers(@NonNull LayerCaptureArgs captureArgs,
            @NonNull ScreenCaptureListener captureListener) {
        return nativeCaptureLayers(captureArgs, captureListener);
    }

    /**
     * @hide
     */
    public interface ScreenCaptureListener {
        /**
         * The callback invoked when the screen capture is complete.
         * @param hardwareBuffer Data containing info about the screen capture.
         */
        void onScreenCaptureComplete(ScreenshotHardwareBuffer hardwareBuffer);
        return nativeCaptureLayers(captureArgs, captureListener.mNativeObject);
    }

    /**
@@ -192,6 +196,7 @@ public class ScreenCapture {

        /**
         * Create ScreenshotHardwareBuffer from an existing HardwareBuffer object.
         *
         * @param hardwareBuffer       The existing HardwareBuffer object
         * @param namedColorSpace      Integer value of a named color space {@link ColorSpace.Named}
         * @param containsSecureLayers Indicates whether this graphic buffer contains captured
@@ -220,6 +225,7 @@ public class ScreenCapture {
        public boolean containsSecureLayers() {
            return mContainsSecureLayers;
        }

        /**
         * Returns whether the screenshot contains at least one HDR layer.
         * This information may be useful for informing the display whether this screenshot
@@ -234,7 +240,7 @@ public class ScreenCapture {
         * Note: If you want to modify the Bitmap in software, you will need to copy the Bitmap
         * into
         * a software Bitmap using {@link Bitmap#copy(Bitmap.Config, boolean)}
         *
         * <p>
         * CAVEAT: This can be extremely slow; avoid use unless absolutely necessary; prefer to
         * directly
         * use the {@link HardwareBuffer} directly.
@@ -250,44 +256,23 @@ public class ScreenCapture {
        }
    }

    private static class SyncScreenCaptureListener implements ScreenCaptureListener {
        private static final int SCREENSHOT_WAIT_TIME_S = 1;
        private ScreenshotHardwareBuffer mScreenshotHardwareBuffer;
        private final CountDownLatch mCountDownLatch = new CountDownLatch(1);

        @Override
        public void onScreenCaptureComplete(ScreenshotHardwareBuffer hardwareBuffer) {
            mScreenshotHardwareBuffer = hardwareBuffer;
            mCountDownLatch.countDown();
        }

        private ScreenshotHardwareBuffer waitForScreenshot() {
            try {
                mCountDownLatch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS);
            } catch (Exception e) {
                Log.e(TAG, "Failed to wait for screen capture result", e);
            }

            return mScreenshotHardwareBuffer;
        }
    }

    /**
     * A common arguments class used for various screenshot requests. This contains arguments that
     * are shared between {@link DisplayCaptureArgs} and {@link LayerCaptureArgs}
     *
     * @hide
     */
    private abstract static class CaptureArgs {
        private final int mPixelFormat;
        private final Rect mSourceCrop = new Rect();
        private final float mFrameScaleX;
        private final float mFrameScaleY;
        private final boolean mCaptureSecureLayers;
        private final boolean mAllowProtected;
        private final long mUid;
        private final boolean mGrayscale;

        private CaptureArgs(Builder<? extends Builder<?>> builder) {
    public static class CaptureArgs implements Parcelable {
        public final int mPixelFormat;
        public final Rect mSourceCrop = new Rect();
        public final float mFrameScaleX;
        public final float mFrameScaleY;
        public final boolean mCaptureSecureLayers;
        public final boolean mAllowProtected;
        public final long mUid;
        public final boolean mGrayscale;

        private CaptureArgs(CaptureArgs.Builder<? extends CaptureArgs.Builder<?>> builder) {
            mPixelFormat = builder.mPixelFormat;
            mSourceCrop.set(builder.mSourceCrop);
            mFrameScaleX = builder.mFrameScaleX;
@@ -298,12 +283,23 @@ public class ScreenCapture {
            mGrayscale = builder.mGrayscale;
        }

        private CaptureArgs(Parcel in) {
            mPixelFormat = in.readInt();
            mSourceCrop.readFromParcel(in);
            mFrameScaleX = in.readFloat();
            mFrameScaleY = in.readFloat();
            mCaptureSecureLayers = in.readBoolean();
            mAllowProtected = in.readBoolean();
            mUid = in.readLong();
            mGrayscale = in.readBoolean();
        }

        /**
         * The Builder class used to construct {@link CaptureArgs}
         *
         * @param <T> A builder that extends {@link Builder}
         * @param <T> A builder that extends {@link CaptureArgs.Builder}
         */
        abstract static class Builder<T extends Builder<T>> {
        public static class Builder<T extends CaptureArgs.Builder<T>> {
            private int mPixelFormat = PixelFormat.RGBA_8888;
            private final Rect mSourceCrop = new Rect();
            private float mFrameScaleX = 1;
@@ -313,6 +309,14 @@ public class ScreenCapture {
            private long mUid = -1;
            private boolean mGrayscale;

            /**
             * Construct a new {@link CaptureArgs} with the set parameters. The builder remains
             * valid.
             */
            public CaptureArgs build() {
                return new CaptureArgs(this);
            }

            /**
             * The desired pixel format of the returned buffer.
             */
@@ -395,15 +399,47 @@ public class ScreenCapture {
            /**
             * Each sub class should return itself to allow the builder to chain properly
             */
            abstract T getThis();
            T getThis() {
                return (T) this;
            }
        }

        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public void writeToParcel(@NonNull Parcel dest, int flags) {
            dest.writeInt(mPixelFormat);
            mSourceCrop.writeToParcel(dest, flags);
            dest.writeFloat(mFrameScaleX);
            dest.writeFloat(mFrameScaleY);
            dest.writeBoolean(mCaptureSecureLayers);
            dest.writeBoolean(mAllowProtected);
            dest.writeLong(mUid);
            dest.writeBoolean(mGrayscale);
        }

        public static final Parcelable.Creator<CaptureArgs> CREATOR =
                new Parcelable.Creator<CaptureArgs>() {
                    @Override
                    public CaptureArgs createFromParcel(Parcel in) {
                        return new CaptureArgs(in);
                    }

                    @Override
                    public CaptureArgs[] newArray(int size) {
                        return new CaptureArgs[size];
                    }
                };
    }

    /**
     * The arguments class used to make display capture requests.
     *
     * @see #nativeCaptureDisplay(DisplayCaptureArgs, ScreenCaptureListener)
     * @hide
     * @see #nativeCaptureDisplay(DisplayCaptureArgs, long)
     */
    public static class DisplayCaptureArgs extends CaptureArgs {
        private final IBinder mDisplayToken;
@@ -488,8 +524,8 @@ public class ScreenCapture {
    /**
     * The arguments class used to make layer capture requests.
     *
     * @see #nativeCaptureLayers(LayerCaptureArgs, ScreenCaptureListener)
     * @hide
     * @see #nativeCaptureLayers(LayerCaptureArgs, long)
     */
    public static class LayerCaptureArgs extends CaptureArgs {
        private final long mNativeLayer;
@@ -530,6 +566,17 @@ public class ScreenCapture {
                return new LayerCaptureArgs(this);
            }

            public Builder(SurfaceControl layer, CaptureArgs args) {
                setLayer(layer);
                setPixelFormat(args.mPixelFormat);
                setSourceCrop(args.mSourceCrop);
                setFrameScale(args.mFrameScaleX, args.mFrameScaleY);
                setCaptureSecureLayers(args.mCaptureSecureLayers);
                setAllowProtected(args.mAllowProtected);
                setUid(args.mUid);
                setGrayscale(args.mGrayscale);
            }

            public Builder(SurfaceControl layer) {
                setLayer(layer);
            }
@@ -542,7 +589,6 @@ public class ScreenCapture {
                return this;
            }


            /**
             * An array of layer handles to exclude.
             */
@@ -564,8 +610,109 @@ public class ScreenCapture {
            Builder getThis() {
                return this;
            }
        }
    }

    /**
     * The object used to receive the results when invoking screen capture requests via
     * {@link #captureDisplay(DisplayCaptureArgs, ScreenCaptureListener)} or
     * {@link #captureLayers(LayerCaptureArgs, ScreenCaptureListener)}
     *
     * This listener can only be used for a single call to capture content call.
     */
    public static class ScreenCaptureListener implements Parcelable {
        private final long mNativeObject;
        private static final NativeAllocationRegistry sRegistry =
                NativeAllocationRegistry.createMalloced(
                        ScreenCaptureListener.class.getClassLoader(), getNativeListenerFinalizer());

        /**
         * @param consumer The callback invoked when the screen capture is complete.
         */
        public ScreenCaptureListener(Consumer<ScreenshotHardwareBuffer> consumer) {
            mNativeObject = nativeCreateScreenCaptureListener(consumer);
            sRegistry.registerNativeAllocation(this, mNativeObject);
        }

        private ScreenCaptureListener(Parcel in) {
            if (in.readBoolean()) {
                mNativeObject = nativeReadListenerFromParcel(in);
                sRegistry.registerNativeAllocation(this, mNativeObject);
            } else {
                mNativeObject = 0;
            }
        }

        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public void writeToParcel(@NonNull Parcel dest, int flags) {
            if (mNativeObject == 0) {
                dest.writeBoolean(false);
            } else {
                dest.writeBoolean(true);
                nativeWriteListenerToParcel(mNativeObject, dest);
            }
        }

        public static final Parcelable.Creator<ScreenCaptureListener> CREATOR =
                new Parcelable.Creator<ScreenCaptureListener>() {
                    @Override
                    public ScreenCaptureListener createFromParcel(Parcel in) {
                        return new ScreenCaptureListener(in);
                    }

                    @Override
                    public ScreenCaptureListener[] newArray(int size) {
                        return new ScreenCaptureListener[0];
                    }
                };
    }

    /**
     * A helper method to handle the async screencapture callbacks synchronously. This should only
     * be used if the screencapture caller doesn't care that it blocks waiting for a screenshot.
     *
     * @return a Pair that holds the {@link ScreenCaptureListener} that should be used for capture
     * calls into SurfaceFlinger and a {@link ScreenshotSync} object to retrieve the results.
     */
    public static Pair<ScreenCaptureListener, ScreenshotSync> createSyncCaptureListener() {
        final ScreenshotSync screenshotSync = new ScreenshotSync();
        final ScreenCaptureListener screenCaptureListener = new ScreenCaptureListener(
                screenshotSync::setScreenshotHardwareBuffer);
        return new Pair<>(screenCaptureListener, screenshotSync);
    }

    /**
     * Helper class to synchronously get the {@link ScreenshotHardwareBuffer} when calling
     * {@link #captureLayers(LayerCaptureArgs, ScreenCaptureListener)} or
     * {@link #captureDisplay(DisplayCaptureArgs, ScreenCaptureListener)}
     */
    public static class ScreenshotSync {
        private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
        private ScreenshotHardwareBuffer mScreenshotHardwareBuffer;

        private void setScreenshotHardwareBuffer(
                ScreenshotHardwareBuffer screenshotHardwareBuffer) {
            mScreenshotHardwareBuffer = screenshotHardwareBuffer;
            mCountDownLatch.countDown();
        }

        /**
         * Get the {@link ScreenshotHardwareBuffer} synchronously. This can be null if the
         * screenshot failed or if there was no callback in {@link #SCREENSHOT_WAIT_TIME_S} seconds.
         */
        public ScreenshotHardwareBuffer get() {
            try {
                mCountDownLatch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS);
                return mScreenshotHardwareBuffer;
            } catch (Exception e) {
                Log.e(TAG, "Failed to wait for screen capture result", e);
                return null;
            }
        }
    }
}
+73 −32

File changed.

Preview size limit exceeded, changes collapsed.

+42 −0
Original line number Diff line number Diff line
@@ -9286,4 +9286,46 @@ public class WindowManagerService extends IWindowManager.Stub
                        "Unexpected letterbox background type: " + letterboxBackgroundType);
        }
    }

    @Override
    public void captureDisplay(int displayId, @Nullable ScreenCapture.CaptureArgs captureArgs,
            ScreenCapture.ScreenCaptureListener listener) {
        Slog.d(TAG, "captureDisplay");
        if (!checkCallingPermission(READ_FRAME_BUFFER, "captureDisplay()")) {
            throw new SecurityException("Requires READ_FRAME_BUFFER permission");
        }

        ScreenCapture.captureLayers(getCaptureArgs(displayId, captureArgs), listener);
    }

    @VisibleForTesting
    ScreenCapture.LayerCaptureArgs getCaptureArgs(int displayId,
            @Nullable ScreenCapture.CaptureArgs captureArgs) {
        final SurfaceControl displaySurfaceControl;
        synchronized (mGlobalLock) {
            DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent == null) {
                throw new IllegalArgumentException("Trying to screenshot and invalid display: "
                        + displayId);
            }

            displaySurfaceControl = displayContent.getSurfaceControl();

            if (captureArgs == null) {
                captureArgs = new ScreenCapture.CaptureArgs.Builder<>()
                        .build();
            }

            if (captureArgs.mSourceCrop.isEmpty()) {
                displayContent.getBounds(mTmpRect);
                mTmpRect.offsetTo(0, 0);
            } else {
                mTmpRect.set(captureArgs.mSourceCrop);
            }
        }

        return new ScreenCapture.LayerCaptureArgs.Builder(displaySurfaceControl, captureArgs)
                        .setSourceCrop(mTmpRect)
                        .build();
    }
}
Loading