Loading core/java/android/view/IWindowManager.aidl +8 −0 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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); } core/java/android/window/ScreenCapture.aidl 0 → 100644 +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 core/java/android/window/ScreenCapture.java +241 −94 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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 Loading @@ -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); } /** Loading @@ -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; } } /** Loading @@ -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 */ Loading @@ -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 */ Loading @@ -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, Loading @@ -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); } /** Loading @@ -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 Loading Loading @@ -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 Loading @@ -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. Loading @@ -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; Loading @@ -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; Loading @@ -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. */ Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); } Loading @@ -542,7 +589,6 @@ public class ScreenCapture { return this; } /** * An array of layer handles to exclude. */ Loading @@ -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; } } } } core/jni/android_window_ScreenCapture.cpp +73 −32 File changed.Preview size limit exceeded, changes collapsed. Show changes services/core/java/com/android/server/wm/WindowManagerService.java +42 −0 Original line number Diff line number Diff line Loading @@ -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
core/java/android/view/IWindowManager.aidl +8 −0 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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); }
core/java/android/window/ScreenCapture.aidl 0 → 100644 +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
core/java/android/window/ScreenCapture.java +241 −94 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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 Loading @@ -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); } /** Loading @@ -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; } } /** Loading @@ -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 */ Loading @@ -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 */ Loading @@ -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, Loading @@ -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); } /** Loading @@ -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 Loading Loading @@ -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 Loading @@ -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. Loading @@ -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; Loading @@ -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; Loading @@ -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. */ Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); } Loading @@ -542,7 +589,6 @@ public class ScreenCapture { return this; } /** * An array of layer handles to exclude. */ Loading @@ -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; } } } }
core/jni/android_window_ScreenCapture.cpp +73 −32 File changed.Preview size limit exceeded, changes collapsed. Show changes
services/core/java/com/android/server/wm/WindowManagerService.java +42 −0 Original line number Diff line number Diff line Loading @@ -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(); } }