Loading core/java/android/view/IWindowManager.aidl +4 −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,7 @@ interface IWindowManager * treatment. */ boolean isLetterboxBackgroundMultiColored(); 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 +215 −79 Original line number Diff line number Diff line Loading @@ -24,11 +24,16 @@ 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.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 @@ -39,9 +44,14 @@ public class ScreenCapture { private static final String TAG = "ScreenCapture"; 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 +60,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,10 +71,8 @@ public class ScreenCapture { */ public static ScreenshotHardwareBuffer captureDisplay( DisplayCaptureArgs captureArgs) { SyncScreenCaptureListener screenCaptureListener = new SyncScreenCaptureListener(); int status = captureDisplay(captureArgs, screenCaptureListener); SyncScreenCaptureListener screenCaptureListener = new SyncScreenCaptureListener(); int status = captureDisplay(captureArgs, screenCaptureListener.getScreenCaptureListener()); if (status != 0) { return null; } Loading @@ -80,9 +88,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 +106,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 @@ -124,7 +130,7 @@ public class ScreenCapture { LayerCaptureArgs captureArgs) { SyncScreenCaptureListener screenCaptureListener = new SyncScreenCaptureListener(); int status = captureLayers(captureArgs, screenCaptureListener); int status = captureLayers(captureArgs, screenCaptureListener.getScreenCaptureListener()); if (status != 0) { return null; } Loading @@ -135,6 +141,7 @@ public class ScreenCapture { /** * 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 +163,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 +188,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 +217,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 +232,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,34 +248,13 @@ 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 { public static class CaptureArgs implements Parcelable { private final int mPixelFormat; private final Rect mSourceCrop = new Rect(); private final float mFrameScaleX; Loading @@ -287,7 +264,7 @@ public class ScreenCapture { private final long mUid; private final boolean mGrayscale; private CaptureArgs(Builder<? extends Builder<?>> builder) { private CaptureArgs(CaptureArgs.Builder<? extends CaptureArgs.Builder<?>> builder) { mPixelFormat = builder.mPixelFormat; mSourceCrop.set(builder.mSourceCrop); mFrameScaleX = builder.mFrameScaleX; Loading @@ -298,12 +275,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 +301,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 +391,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 +516,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 +558,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 +581,6 @@ public class ScreenCapture { return this; } /** * An array of layer handles to exclude. */ Loading @@ -564,8 +602,106 @@ 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)} */ 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 class 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. */ public static class SyncScreenCaptureListener { private static final int SCREENSHOT_WAIT_TIME_S = 1; private ScreenshotHardwareBuffer mScreenshotHardwareBuffer; private final CountDownLatch mCountDownLatch = new CountDownLatch(1); private final ScreenCaptureListener mScreenCaptureListener; public SyncScreenCaptureListener() { mScreenCaptureListener = new ScreenCaptureListener(screenshotHardwareBuffer -> { mScreenshotHardwareBuffer = screenshotHardwareBuffer; mCountDownLatch.countDown(); }); } /** * @return The underlying {@link ScreenCaptureListener} */ public ScreenCaptureListener getScreenCaptureListener() { return mScreenCaptureListener; } /** * Waits until the screenshot callback has been invoked and the screenshot is ready. This * can return {@code null} if the screenshot callback wasn't invoked after * {@link #SCREENSHOT_WAIT_TIME_S} or the screencapture request resulted in an error * * @return A ScreenshotHardwareBuffer for the content that was captured. */ @Nullable public 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; } } } core/jni/android_window_ScreenCapture.cpp +73 −32 Original line number Diff line number Diff line Loading @@ -61,9 +61,8 @@ static struct { } gLayerCaptureArgsClassInfo; static struct { jclass clazz; jmethodID onScreenCaptureComplete; } gScreenCaptureListenerClassInfo; jmethodID accept; } gConsumerClassInfo; static struct { jclass clazz; Loading Loading @@ -98,14 +97,14 @@ class ScreenCaptureListenerWrapper : public gui::BnScreenCaptureListener { public: explicit ScreenCaptureListenerWrapper(JNIEnv* env, jobject jobject) { env->GetJavaVM(&mVm); mScreenCaptureListenerObject = env->NewGlobalRef(jobject); LOG_ALWAYS_FATAL_IF(!mScreenCaptureListenerObject, "Failed to make global ref"); mConsumerObject = env->NewGlobalRef(jobject); LOG_ALWAYS_FATAL_IF(!mConsumerObject, "Failed to make global ref"); } ~ScreenCaptureListenerWrapper() { if (mScreenCaptureListenerObject) { getenv()->DeleteGlobalRef(mScreenCaptureListenerObject); mScreenCaptureListenerObject = nullptr; if (mConsumerObject) { getenv()->DeleteGlobalRef(mConsumerObject); mConsumerObject = nullptr; } } Loading @@ -113,9 +112,8 @@ public: const gui::ScreenCaptureResults& captureResults) override { JNIEnv* env = getenv(); if (!captureResults.fenceResult.ok() || captureResults.buffer == nullptr) { env->CallVoidMethod(mScreenCaptureListenerObject, gScreenCaptureListenerClassInfo.onScreenCaptureComplete, nullptr); checkAndClearException(env, "onScreenCaptureComplete"); env->CallVoidMethod(mConsumerObject, gConsumerClassInfo.accept, nullptr); checkAndClearException(env, "accept"); return binder::Status::ok(); } captureResults.fenceResult.value()->waitForever(LOG_TAG); Loading @@ -130,17 +128,15 @@ public: captureResults.capturedSecureLayers, captureResults.capturedHdrLayers); checkAndClearException(env, "builder"); env->CallVoidMethod(mScreenCaptureListenerObject, gScreenCaptureListenerClassInfo.onScreenCaptureComplete, screenshotHardwareBuffer); checkAndClearException(env, "onScreenCaptureComplete"); env->CallVoidMethod(mConsumerObject, gConsumerClassInfo.accept, screenshotHardwareBuffer); checkAndClearException(env, "accept"); env->DeleteLocalRef(jhardwareBuffer); env->DeleteLocalRef(screenshotHardwareBuffer); return binder::Status::ok(); } private: jobject mScreenCaptureListenerObject; jobject mConsumerObject; JavaVM* mVm; JNIEnv* getenv() { Loading Loading @@ -194,7 +190,7 @@ static DisplayCaptureArgs displayCaptureArgsFromObject(JNIEnv* env, } static jint nativeCaptureDisplay(JNIEnv* env, jclass clazz, jobject displayCaptureArgsObject, jobject screenCaptureListenerObject) { jlong screenCaptureListenerObject) { const DisplayCaptureArgs captureArgs = displayCaptureArgsFromObject(env, displayCaptureArgsObject); Loading @@ -202,13 +198,13 @@ static jint nativeCaptureDisplay(JNIEnv* env, jclass clazz, jobject displayCaptu return BAD_VALUE; } sp<IScreenCaptureListener> captureListener = sp<ScreenCaptureListenerWrapper>::make(env, screenCaptureListenerObject); sp<gui::IScreenCaptureListener> captureListener = reinterpret_cast<gui::IScreenCaptureListener*>(screenCaptureListenerObject); return ScreenshotClient::captureDisplay(captureArgs, captureListener); } static jint nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureArgsObject, jobject screenCaptureListenerObject) { jlong screenCaptureListenerObject) { LayerCaptureArgs captureArgs; getCaptureArgs(env, layerCaptureArgsObject, captureArgs); SurfaceControl* layer = reinterpret_cast<SurfaceControl*>( Loading Loading @@ -238,21 +234,70 @@ static jint nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureA } } sp<IScreenCaptureListener> captureListener = sp<ScreenCaptureListenerWrapper>::make(env, screenCaptureListenerObject); sp<gui::IScreenCaptureListener> captureListener = reinterpret_cast<gui::IScreenCaptureListener*>(screenCaptureListenerObject); return ScreenshotClient::captureLayers(captureArgs, captureListener); } static jlong nativeCreateScreenCaptureListener(JNIEnv* env, jclass clazz, jobject consumerObj) { sp<gui::IScreenCaptureListener> listener = sp<ScreenCaptureListenerWrapper>::make(env, consumerObj); listener->incStrong((void*)nativeCreateScreenCaptureListener); return reinterpret_cast<jlong>(listener.get()); } static void nativeWriteListenerToParcel(JNIEnv* env, jclass clazz, jlong nativeObject, jobject parcelObj) { Parcel* parcel = parcelForJavaObject(env, parcelObj); if (parcel == NULL) { jniThrowNullPointerException(env, NULL); return; } ScreenCaptureListenerWrapper* const self = reinterpret_cast<ScreenCaptureListenerWrapper*>(nativeObject); if (self != nullptr) { parcel->writeStrongBinder(IInterface::asBinder(self)); } } static jlong nativeReadListenerFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) { Parcel* parcel = parcelForJavaObject(env, parcelObj); if (parcel == NULL) { jniThrowNullPointerException(env, NULL); return 0; } sp<gui::IScreenCaptureListener> listener = interface_cast<gui::IScreenCaptureListener>(parcel->readStrongBinder()); if (listener == nullptr) { return 0; } listener->incStrong((void*)nativeCreateScreenCaptureListener); return reinterpret_cast<jlong>(listener.get()); } void destroyNativeListener(void* ptr) { ScreenCaptureListenerWrapper* listener = reinterpret_cast<ScreenCaptureListenerWrapper*>(ptr); listener->decStrong((void*)nativeCreateScreenCaptureListener); } static jlong getNativeListenerFinalizer(JNIEnv* env, jclass clazz) { return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyNativeListener)); } // ---------------------------------------------------------------------------- static const JNINativeMethod sScreenCaptureMethods[] = { // clang-format off {"nativeCaptureDisplay", "(Landroid/window/ScreenCapture$DisplayCaptureArgs;Landroid/window/ScreenCapture$ScreenCaptureListener;)I", {"nativeCaptureDisplay", "(Landroid/window/ScreenCapture$DisplayCaptureArgs;J)I", (void*)nativeCaptureDisplay }, {"nativeCaptureLayers", "(Landroid/window/ScreenCapture$LayerCaptureArgs;Landroid/window/ScreenCapture$ScreenCaptureListener;)I", {"nativeCaptureLayers", "(Landroid/window/ScreenCapture$LayerCaptureArgs;J)I", (void*)nativeCaptureLayers }, {"nativeCreateScreenCaptureListener", "(Ljava/util/function/Consumer;)J", (void*)nativeCreateScreenCaptureListener }, {"nativeWriteListenerToParcel", "(JLandroid/os/Parcel;)V", (void*)nativeWriteListenerToParcel }, {"nativeReadListenerFromParcel", "(Landroid/os/Parcel;)J", (void*)nativeReadListenerFromParcel }, {"getNativeListenerFinalizer", "()J", (void*)getNativeListenerFinalizer }, // clang-format on }; Loading Loading @@ -293,12 +338,8 @@ int register_android_window_ScreenCapture(JNIEnv* env) { gLayerCaptureArgsClassInfo.childrenOnly = GetFieldIDOrDie(env, layerCaptureArgsClazz, "mChildrenOnly", "Z"); jclass screenCaptureListenerClazz = FindClassOrDie(env, "android/window/ScreenCapture$ScreenCaptureListener"); gScreenCaptureListenerClassInfo.clazz = MakeGlobalRefOrDie(env, screenCaptureListenerClazz); gScreenCaptureListenerClassInfo.onScreenCaptureComplete = GetMethodIDOrDie(env, screenCaptureListenerClazz, "onScreenCaptureComplete", "(Landroid/window/ScreenCapture$ScreenshotHardwareBuffer;)V"); jclass consumer = FindClassOrDie(env, "java/util/function/Consumer"); gConsumerClassInfo.accept = GetMethodIDOrDie(env, consumer, "accept", "(Ljava/lang/Object;)V"); jclass screenshotGraphicsBufferClazz = FindClassOrDie(env, "android/window/ScreenCapture$ScreenshotHardwareBuffer"); Loading services/core/java/com/android/server/wm/WindowManagerService.java +34 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/android/view/IWindowManager.aidl +4 −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,7 @@ interface IWindowManager * treatment. */ boolean isLetterboxBackgroundMultiColored(); 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 +215 −79 Original line number Diff line number Diff line Loading @@ -24,11 +24,16 @@ 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.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 @@ -39,9 +44,14 @@ public class ScreenCapture { private static final String TAG = "ScreenCapture"; 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 +60,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,10 +71,8 @@ public class ScreenCapture { */ public static ScreenshotHardwareBuffer captureDisplay( DisplayCaptureArgs captureArgs) { SyncScreenCaptureListener screenCaptureListener = new SyncScreenCaptureListener(); int status = captureDisplay(captureArgs, screenCaptureListener); SyncScreenCaptureListener screenCaptureListener = new SyncScreenCaptureListener(); int status = captureDisplay(captureArgs, screenCaptureListener.getScreenCaptureListener()); if (status != 0) { return null; } Loading @@ -80,9 +88,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 +106,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 @@ -124,7 +130,7 @@ public class ScreenCapture { LayerCaptureArgs captureArgs) { SyncScreenCaptureListener screenCaptureListener = new SyncScreenCaptureListener(); int status = captureLayers(captureArgs, screenCaptureListener); int status = captureLayers(captureArgs, screenCaptureListener.getScreenCaptureListener()); if (status != 0) { return null; } Loading @@ -135,6 +141,7 @@ public class ScreenCapture { /** * 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 +163,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 +188,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 +217,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 +232,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,34 +248,13 @@ 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 { public static class CaptureArgs implements Parcelable { private final int mPixelFormat; private final Rect mSourceCrop = new Rect(); private final float mFrameScaleX; Loading @@ -287,7 +264,7 @@ public class ScreenCapture { private final long mUid; private final boolean mGrayscale; private CaptureArgs(Builder<? extends Builder<?>> builder) { private CaptureArgs(CaptureArgs.Builder<? extends CaptureArgs.Builder<?>> builder) { mPixelFormat = builder.mPixelFormat; mSourceCrop.set(builder.mSourceCrop); mFrameScaleX = builder.mFrameScaleX; Loading @@ -298,12 +275,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 +301,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 +391,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 +516,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 +558,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 +581,6 @@ public class ScreenCapture { return this; } /** * An array of layer handles to exclude. */ Loading @@ -564,8 +602,106 @@ 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)} */ 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 class 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. */ public static class SyncScreenCaptureListener { private static final int SCREENSHOT_WAIT_TIME_S = 1; private ScreenshotHardwareBuffer mScreenshotHardwareBuffer; private final CountDownLatch mCountDownLatch = new CountDownLatch(1); private final ScreenCaptureListener mScreenCaptureListener; public SyncScreenCaptureListener() { mScreenCaptureListener = new ScreenCaptureListener(screenshotHardwareBuffer -> { mScreenshotHardwareBuffer = screenshotHardwareBuffer; mCountDownLatch.countDown(); }); } /** * @return The underlying {@link ScreenCaptureListener} */ public ScreenCaptureListener getScreenCaptureListener() { return mScreenCaptureListener; } /** * Waits until the screenshot callback has been invoked and the screenshot is ready. This * can return {@code null} if the screenshot callback wasn't invoked after * {@link #SCREENSHOT_WAIT_TIME_S} or the screencapture request resulted in an error * * @return A ScreenshotHardwareBuffer for the content that was captured. */ @Nullable public 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; } } }
core/jni/android_window_ScreenCapture.cpp +73 −32 Original line number Diff line number Diff line Loading @@ -61,9 +61,8 @@ static struct { } gLayerCaptureArgsClassInfo; static struct { jclass clazz; jmethodID onScreenCaptureComplete; } gScreenCaptureListenerClassInfo; jmethodID accept; } gConsumerClassInfo; static struct { jclass clazz; Loading Loading @@ -98,14 +97,14 @@ class ScreenCaptureListenerWrapper : public gui::BnScreenCaptureListener { public: explicit ScreenCaptureListenerWrapper(JNIEnv* env, jobject jobject) { env->GetJavaVM(&mVm); mScreenCaptureListenerObject = env->NewGlobalRef(jobject); LOG_ALWAYS_FATAL_IF(!mScreenCaptureListenerObject, "Failed to make global ref"); mConsumerObject = env->NewGlobalRef(jobject); LOG_ALWAYS_FATAL_IF(!mConsumerObject, "Failed to make global ref"); } ~ScreenCaptureListenerWrapper() { if (mScreenCaptureListenerObject) { getenv()->DeleteGlobalRef(mScreenCaptureListenerObject); mScreenCaptureListenerObject = nullptr; if (mConsumerObject) { getenv()->DeleteGlobalRef(mConsumerObject); mConsumerObject = nullptr; } } Loading @@ -113,9 +112,8 @@ public: const gui::ScreenCaptureResults& captureResults) override { JNIEnv* env = getenv(); if (!captureResults.fenceResult.ok() || captureResults.buffer == nullptr) { env->CallVoidMethod(mScreenCaptureListenerObject, gScreenCaptureListenerClassInfo.onScreenCaptureComplete, nullptr); checkAndClearException(env, "onScreenCaptureComplete"); env->CallVoidMethod(mConsumerObject, gConsumerClassInfo.accept, nullptr); checkAndClearException(env, "accept"); return binder::Status::ok(); } captureResults.fenceResult.value()->waitForever(LOG_TAG); Loading @@ -130,17 +128,15 @@ public: captureResults.capturedSecureLayers, captureResults.capturedHdrLayers); checkAndClearException(env, "builder"); env->CallVoidMethod(mScreenCaptureListenerObject, gScreenCaptureListenerClassInfo.onScreenCaptureComplete, screenshotHardwareBuffer); checkAndClearException(env, "onScreenCaptureComplete"); env->CallVoidMethod(mConsumerObject, gConsumerClassInfo.accept, screenshotHardwareBuffer); checkAndClearException(env, "accept"); env->DeleteLocalRef(jhardwareBuffer); env->DeleteLocalRef(screenshotHardwareBuffer); return binder::Status::ok(); } private: jobject mScreenCaptureListenerObject; jobject mConsumerObject; JavaVM* mVm; JNIEnv* getenv() { Loading Loading @@ -194,7 +190,7 @@ static DisplayCaptureArgs displayCaptureArgsFromObject(JNIEnv* env, } static jint nativeCaptureDisplay(JNIEnv* env, jclass clazz, jobject displayCaptureArgsObject, jobject screenCaptureListenerObject) { jlong screenCaptureListenerObject) { const DisplayCaptureArgs captureArgs = displayCaptureArgsFromObject(env, displayCaptureArgsObject); Loading @@ -202,13 +198,13 @@ static jint nativeCaptureDisplay(JNIEnv* env, jclass clazz, jobject displayCaptu return BAD_VALUE; } sp<IScreenCaptureListener> captureListener = sp<ScreenCaptureListenerWrapper>::make(env, screenCaptureListenerObject); sp<gui::IScreenCaptureListener> captureListener = reinterpret_cast<gui::IScreenCaptureListener*>(screenCaptureListenerObject); return ScreenshotClient::captureDisplay(captureArgs, captureListener); } static jint nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureArgsObject, jobject screenCaptureListenerObject) { jlong screenCaptureListenerObject) { LayerCaptureArgs captureArgs; getCaptureArgs(env, layerCaptureArgsObject, captureArgs); SurfaceControl* layer = reinterpret_cast<SurfaceControl*>( Loading Loading @@ -238,21 +234,70 @@ static jint nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureA } } sp<IScreenCaptureListener> captureListener = sp<ScreenCaptureListenerWrapper>::make(env, screenCaptureListenerObject); sp<gui::IScreenCaptureListener> captureListener = reinterpret_cast<gui::IScreenCaptureListener*>(screenCaptureListenerObject); return ScreenshotClient::captureLayers(captureArgs, captureListener); } static jlong nativeCreateScreenCaptureListener(JNIEnv* env, jclass clazz, jobject consumerObj) { sp<gui::IScreenCaptureListener> listener = sp<ScreenCaptureListenerWrapper>::make(env, consumerObj); listener->incStrong((void*)nativeCreateScreenCaptureListener); return reinterpret_cast<jlong>(listener.get()); } static void nativeWriteListenerToParcel(JNIEnv* env, jclass clazz, jlong nativeObject, jobject parcelObj) { Parcel* parcel = parcelForJavaObject(env, parcelObj); if (parcel == NULL) { jniThrowNullPointerException(env, NULL); return; } ScreenCaptureListenerWrapper* const self = reinterpret_cast<ScreenCaptureListenerWrapper*>(nativeObject); if (self != nullptr) { parcel->writeStrongBinder(IInterface::asBinder(self)); } } static jlong nativeReadListenerFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) { Parcel* parcel = parcelForJavaObject(env, parcelObj); if (parcel == NULL) { jniThrowNullPointerException(env, NULL); return 0; } sp<gui::IScreenCaptureListener> listener = interface_cast<gui::IScreenCaptureListener>(parcel->readStrongBinder()); if (listener == nullptr) { return 0; } listener->incStrong((void*)nativeCreateScreenCaptureListener); return reinterpret_cast<jlong>(listener.get()); } void destroyNativeListener(void* ptr) { ScreenCaptureListenerWrapper* listener = reinterpret_cast<ScreenCaptureListenerWrapper*>(ptr); listener->decStrong((void*)nativeCreateScreenCaptureListener); } static jlong getNativeListenerFinalizer(JNIEnv* env, jclass clazz) { return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyNativeListener)); } // ---------------------------------------------------------------------------- static const JNINativeMethod sScreenCaptureMethods[] = { // clang-format off {"nativeCaptureDisplay", "(Landroid/window/ScreenCapture$DisplayCaptureArgs;Landroid/window/ScreenCapture$ScreenCaptureListener;)I", {"nativeCaptureDisplay", "(Landroid/window/ScreenCapture$DisplayCaptureArgs;J)I", (void*)nativeCaptureDisplay }, {"nativeCaptureLayers", "(Landroid/window/ScreenCapture$LayerCaptureArgs;Landroid/window/ScreenCapture$ScreenCaptureListener;)I", {"nativeCaptureLayers", "(Landroid/window/ScreenCapture$LayerCaptureArgs;J)I", (void*)nativeCaptureLayers }, {"nativeCreateScreenCaptureListener", "(Ljava/util/function/Consumer;)J", (void*)nativeCreateScreenCaptureListener }, {"nativeWriteListenerToParcel", "(JLandroid/os/Parcel;)V", (void*)nativeWriteListenerToParcel }, {"nativeReadListenerFromParcel", "(Landroid/os/Parcel;)J", (void*)nativeReadListenerFromParcel }, {"getNativeListenerFinalizer", "()J", (void*)getNativeListenerFinalizer }, // clang-format on }; Loading Loading @@ -293,12 +338,8 @@ int register_android_window_ScreenCapture(JNIEnv* env) { gLayerCaptureArgsClassInfo.childrenOnly = GetFieldIDOrDie(env, layerCaptureArgsClazz, "mChildrenOnly", "Z"); jclass screenCaptureListenerClazz = FindClassOrDie(env, "android/window/ScreenCapture$ScreenCaptureListener"); gScreenCaptureListenerClassInfo.clazz = MakeGlobalRefOrDie(env, screenCaptureListenerClazz); gScreenCaptureListenerClassInfo.onScreenCaptureComplete = GetMethodIDOrDie(env, screenCaptureListenerClazz, "onScreenCaptureComplete", "(Landroid/window/ScreenCapture$ScreenshotHardwareBuffer;)V"); jclass consumer = FindClassOrDie(env, "java/util/function/Consumer"); gConsumerClassInfo.accept = GetMethodIDOrDie(env, consumer, "accept", "(Ljava/lang/Object;)V"); jclass screenshotGraphicsBufferClazz = FindClassOrDie(env, "android/window/ScreenCapture$ScreenshotHardwareBuffer"); Loading
services/core/java/com/android/server/wm/WindowManagerService.java +34 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes