diff options
author | chaviw <chaviw@google.com> | 2020-08-18 16:06:40 -0700 |
---|---|---|
committer | chaviw <chaviw@google.com> | 2020-09-08 09:59:50 -0700 |
commit | 3510924a6de6ad44160de44051d28df87593edd3 (patch) | |
tree | 1d547c3c90b165a509d26a7f9deeb70783224791 | |
parent | 25953d498f68ac3cdc3b7f4c0a33946725ac960d (diff) |
Send ScreenCaptureListener to native screen capture requests.
Allow for asynchronous screenshot by sending a ScreenCaptureListener to
handle screenshot callbacks. All existing calls are still synchronous
since the SurfaceControl method will block the caller until the results
from SurfaceFlinger are ready.
Test: power + volume down
Test: adb shell screencap
Bug: 162367424
Change-Id: I54c34003c0786b585dd20530a06dbd4b266e178c
-rw-r--r-- | cmds/screencap/screencap.cpp | 12 | ||||
-rw-r--r-- | core/java/android/view/SurfaceControl.java | 96 | ||||
-rw-r--r-- | core/jni/android_view_SurfaceControl.cpp | 110 |
3 files changed, 172 insertions, 46 deletions
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp index dec4a567fc81..5c08704a6623 100644 --- a/cmds/screencap/screencap.cpp +++ b/cmds/screencap/screencap.cpp @@ -30,8 +30,9 @@ #include <binder/ProcessState.h> -#include <gui/SurfaceComposerClient.h> #include <gui/ISurfaceComposer.h> +#include <gui/SurfaceComposerClient.h> +#include <gui/SyncScreenCaptureListener.h> #include <ui/DisplayInfo.h> #include <ui/GraphicTypes.h> @@ -181,13 +182,18 @@ int main(int argc, char** argv) ProcessState::self()->setThreadPoolMaxThreadCount(0); ProcessState::self()->startThreadPool(); - ScreenCaptureResults captureResults; - status_t result = ScreenshotClient::captureDisplay(displayId->value, captureResults); + sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener(); + status_t result = ScreenshotClient::captureDisplay(displayId->value, captureListener); if (result != NO_ERROR) { close(fd); return 1; } + ScreenCaptureResults captureResults = captureListener->waitForResults(); + if (captureResults.result != NO_ERROR) { + close(fd); + return 1; + } ui::Dataspace dataspace = captureResults.capturedDataspace; sp<GraphicBuffer> buffer = captureResults.buffer; diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 6ef086b55c41..3af8958368dc 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -65,6 +65,9 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Objects; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; /** * Handle to an on-screen Surface managed by the system compositor. The SurfaceControl is @@ -87,10 +90,10 @@ public final class SurfaceControl implements Parcelable { private static native void nativeWriteToParcel(long nativeObject, Parcel out); private static native void nativeRelease(long nativeObject); private static native void nativeDisconnect(long nativeObject); - private static native ScreenshotHardwareBuffer nativeCaptureDisplay( - DisplayCaptureArgs captureArgs); - private static native ScreenshotHardwareBuffer nativeCaptureLayers( - LayerCaptureArgs captureArgs); + private static native int nativeCaptureDisplay(DisplayCaptureArgs captureArgs, + ScreenCaptureListener captureListener); + private static native int nativeCaptureLayers(LayerCaptureArgs captureArgs, + ScreenCaptureListener captureListener); private static native long nativeMirrorSurface(long mirrorOfObject); private static native long nativeCreateTransaction(); private static native long nativeGetNativeTransactionFinalizer(); @@ -493,6 +496,8 @@ public final class SurfaceControl implements Parcelable { private static final int INTERNAL_DATASPACE_DISPLAY_P3 = 143261696; private static final int INTERNAL_DATASPACE_SCRGB = 411107328; + private static final int SCREENSHOT_WAIT_TIME_S = 1; + private void assignNativeObject(long nativeObject, String callsite) { if (mNativeObject != 0) { release(); @@ -611,6 +616,13 @@ public final class SurfaceControl implements Parcelable { } /** + * @hide + */ + public abstract static class ScreenCaptureListener { + abstract void onScreenCaptureComplete(ScreenshotHardwareBuffer hardwareBuffer); + } + + /** * A common arguments class used for various screenshot requests. This contains arguments that * are shared between {@link DisplayCaptureArgs} and {@link LayerCaptureArgs} * @hide @@ -685,7 +697,7 @@ public final class SurfaceControl implements Parcelable { /** * The arguments class used to make display capture requests. * - * @see #nativeCaptureDisplay(DisplayCaptureArgs) + * @see #nativeCaptureDisplay(DisplayCaptureArgs, ScreenCaptureListener) * @hide */ public static class DisplayCaptureArgs extends CaptureArgs { @@ -2226,13 +2238,46 @@ public final class SurfaceControl implements Parcelable { } /** + * @param captureArgs Arguments about how to take the screenshot + * @param captureListener A listener to receive the screenshot callback + * @hide + */ + public static int captureDisplay(@NonNull DisplayCaptureArgs captureArgs, + @NonNull ScreenCaptureListener captureListener) { + return nativeCaptureDisplay(captureArgs, captureListener); + } + + /** * Captures all the surfaces in a display and returns a {@link ScreenshotHardwareBuffer} with * the content. * * @hide */ public static ScreenshotHardwareBuffer captureDisplay(DisplayCaptureArgs captureArgs) { - return nativeCaptureDisplay(captureArgs); + final AtomicReference<ScreenshotHardwareBuffer> outHardwareBuffer = + new AtomicReference<>(null); + + final CountDownLatch countDownLatch = new CountDownLatch(1); + ScreenCaptureListener screenCaptureListener = new ScreenCaptureListener() { + @Override + void onScreenCaptureComplete(ScreenshotHardwareBuffer hardwareBuffer) { + outHardwareBuffer.set(hardwareBuffer); + countDownLatch.countDown(); + } + }; + + int status = captureDisplay(captureArgs, screenCaptureListener); + if (status != 0) { + return null; + } + + try { + countDownLatch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS); + } catch (Exception e) { + Log.e(TAG, "Failed to wait for captureDisplay result", e); + } + + return outHardwareBuffer.get(); } /** @@ -2277,14 +2322,37 @@ public final class SurfaceControl implements Parcelable { .setPixelFormat(format) .build(); - return nativeCaptureLayers(captureArgs); + return captureLayers(captureArgs); } /** * @hide */ public static ScreenshotHardwareBuffer captureLayers(LayerCaptureArgs captureArgs) { - return nativeCaptureLayers(captureArgs); + final AtomicReference<ScreenshotHardwareBuffer> outHardwareBuffer = + new AtomicReference<>(null); + + final CountDownLatch countDownLatch = new CountDownLatch(1); + ScreenCaptureListener screenCaptureListener = new ScreenCaptureListener() { + @Override + void onScreenCaptureComplete(ScreenshotHardwareBuffer hardwareBuffer) { + outHardwareBuffer.set(hardwareBuffer); + countDownLatch.countDown(); + } + }; + + int status = captureLayers(captureArgs, screenCaptureListener); + if (status != 0) { + return null; + } + + try { + countDownLatch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS); + } catch (Exception e) { + Log.e(TAG, "Failed to wait for captureLayers result", e); + } + + return outHardwareBuffer.get(); } /** @@ -2301,7 +2369,17 @@ public final class SurfaceControl implements Parcelable { .setExcludeLayers(exclude) .build(); - return nativeCaptureLayers(captureArgs); + return captureLayers(captureArgs); + } + + /** + * @param captureArgs Arguments about how to take the screenshot + * @param captureListener A listener to receive the screenshot callback + * @hide + */ + public static int captureLayers(@NonNull LayerCaptureArgs captureArgs, + @NonNull ScreenCaptureListener captureListener) { + return nativeCaptureLayers(captureArgs, captureListener); } /** diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 85b4fe197980..416f8372fd81 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -30,6 +30,7 @@ #include <android_runtime/android_hardware_HardwareBuffer.h> #include <android_runtime/android_view_Surface.h> #include <android_runtime/android_view_SurfaceSession.h> +#include <gui/IScreenCaptureListener.h> #include <gui/ISurfaceComposer.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> @@ -188,6 +189,11 @@ static struct { static struct { jclass clazz; + jmethodID onScreenCaptureComplete; +} gScreenCaptureListenerClassInfo; + +static struct { + jclass clazz; jmethodID ctor; jfieldID defaultConfig; jfieldID primaryRefreshRateMin; @@ -226,6 +232,54 @@ constexpr ui::Dataspace pickDataspaceFromColorMode(const ui::ColorMode colorMode } } +class ScreenCaptureListenerWrapper : public BnScreenCaptureListener { +public: + explicit ScreenCaptureListenerWrapper(JNIEnv* env, jobject jobject) { + env->GetJavaVM(&mVm); + screenCaptureListenerObject = env->NewGlobalRef(jobject); + LOG_ALWAYS_FATAL_IF(!screenCaptureListenerObject, "Failed to make global ref"); + } + + ~ScreenCaptureListenerWrapper() { + if (screenCaptureListenerObject) { + getenv()->DeleteGlobalRef(screenCaptureListenerObject); + screenCaptureListenerObject = nullptr; + } + } + + status_t onScreenCaptureComplete(const ScreenCaptureResults& captureResults) { + JNIEnv* env = getenv(); + if (captureResults.result != NO_ERROR || captureResults.buffer == nullptr) { + env->CallVoidMethod(screenCaptureListenerObject, + gScreenCaptureListenerClassInfo.onScreenCaptureComplete, nullptr); + return NO_ERROR; + } + jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer( + env, captureResults.buffer->toAHardwareBuffer()); + const jint namedColorSpace = + fromDataspaceToNamedColorSpaceValue(captureResults.capturedDataspace); + jobject screenshotHardwareBuffer = + env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz, + gScreenshotHardwareBufferClassInfo.builder, + jhardwareBuffer, namedColorSpace, + captureResults.capturedSecureLayers); + env->CallVoidMethod(screenCaptureListenerObject, + gScreenCaptureListenerClassInfo.onScreenCaptureComplete, + screenshotHardwareBuffer); + return NO_ERROR; + } + +private: + jobject screenCaptureListenerObject; + JavaVM* mVm; + + JNIEnv* getenv() { + JNIEnv* env; + mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6); + return env; + } +}; + // ---------------------------------------------------------------------------- static jlong nativeCreateTransaction(JNIEnv* env, jclass clazz) { @@ -327,36 +381,28 @@ static DisplayCaptureArgs displayCaptureArgsFromObject(JNIEnv* env, return captureArgs; } -static jobject nativeCaptureDisplay(JNIEnv* env, jclass clazz, jobject displayCaptureArgsObject) { +static jint nativeCaptureDisplay(JNIEnv* env, jclass clazz, jobject displayCaptureArgsObject, + jobject screenCaptureListenerObject) { const DisplayCaptureArgs captureArgs = displayCaptureArgsFromObject(env, displayCaptureArgsObject); if (captureArgs.displayToken == NULL) { - return NULL; - } - - ScreenCaptureResults captureResults; - status_t res = ScreenshotClient::captureDisplay(captureArgs, captureResults); - if (res != NO_ERROR) { - return NULL; + return BAD_VALUE; } - jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer( - env, captureResults.buffer->toAHardwareBuffer()); - const jint namedColorSpace = - fromDataspaceToNamedColorSpaceValue(captureResults.capturedDataspace); - return env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz, - gScreenshotHardwareBufferClassInfo.builder, jhardwareBuffer, - namedColorSpace, captureResults.capturedSecureLayers); + sp<IScreenCaptureListener> captureListener = + new ScreenCaptureListenerWrapper(env, screenCaptureListenerObject); + return ScreenshotClient::captureDisplay(captureArgs, captureListener); } -static jobject nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureArgsObject) { +static jint nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureArgsObject, + jobject screenCaptureListenerObject) { LayerCaptureArgs captureArgs; getCaptureArgs(env, layerCaptureArgsObject, captureArgs); SurfaceControl* layer = reinterpret_cast<SurfaceControl*>( env->GetLongField(layerCaptureArgsObject, gLayerCaptureArgsClassInfo.layer)); if (layer == nullptr) { - return nullptr; + return BAD_VALUE; } captureArgs.layerHandle = layer->getHandle(); @@ -380,19 +426,9 @@ static jobject nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptu env->ReleaseLongArrayElements(excludeObjectArray, const_cast<jlong*>(objects), JNI_ABORT); } - ScreenCaptureResults captureResults; - status_t res = ScreenshotClient::captureLayers(captureArgs, captureResults); - if (res != NO_ERROR) { - return NULL; - } - - jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer( - env, captureResults.buffer->toAHardwareBuffer()); - const jint namedColorSpace = - fromDataspaceToNamedColorSpaceValue(captureResults.capturedDataspace); - return env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz, - gScreenshotHardwareBufferClassInfo.builder, jhardwareBuffer, - namedColorSpace, captureResults.capturedSecureLayers); + sp<IScreenCaptureListener> captureListener = + new ScreenCaptureListenerWrapper(env, screenCaptureListenerObject); + return ScreenshotClient::captureLayers(captureArgs, captureListener); } static void nativeApplyTransaction(JNIEnv* env, jclass clazz, jlong transactionObj, jboolean sync) { @@ -1507,6 +1543,7 @@ static jlong nativeGetHandle(JNIEnv* env, jclass clazz, jlong nativeObject) { // ---------------------------------------------------------------------------- +// clang-format off static const JNINativeMethod sSurfaceControlMethods[] = { {"nativeCreate", "(Landroid/view/SurfaceSession;Ljava/lang/String;IIIIJLandroid/os/Parcel;)J", (void*)nativeCreate }, @@ -1649,12 +1686,10 @@ static const JNINativeMethod sSurfaceControlMethods[] = { {"nativeSetOverrideScalingMode", "(JJI)V", (void*)nativeSetOverrideScalingMode }, {"nativeCaptureDisplay", - "(Landroid/view/SurfaceControl$DisplayCaptureArgs;)" - "Landroid/view/SurfaceControl$ScreenshotHardwareBuffer;", + "(Landroid/view/SurfaceControl$DisplayCaptureArgs;Landroid/view/SurfaceControl$ScreenCaptureListener;)I", (void*)nativeCaptureDisplay }, {"nativeCaptureLayers", - "(Landroid/view/SurfaceControl$LayerCaptureArgs;)" - "Landroid/view/SurfaceControl$ScreenshotHardwareBuffer;", + "(Landroid/view/SurfaceControl$LayerCaptureArgs;Landroid/view/SurfaceControl$ScreenCaptureListener;)I", (void*)nativeCaptureLayers }, {"nativeSetInputWindowInfo", "(JJLandroid/view/InputWindowHandle;)V", (void*)nativeSetInputWindowInfo }, @@ -1688,6 +1723,7 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeGetHandle }, {"nativeSetFixedTransformHint", "(JJI)V", (void*)nativeSetFixedTransformHint}, }; +// clang-format on int register_android_view_SurfaceControl(JNIEnv* env) { @@ -1856,6 +1892,12 @@ int register_android_view_SurfaceControl(JNIEnv* env) gLayerCaptureArgsClassInfo.childrenOnly = GetFieldIDOrDie(env, layerCaptureArgsClazz, "mChildrenOnly", "Z"); + jclass screenCaptureListenerClazz = + FindClassOrDie(env, "android/view/SurfaceControl$ScreenCaptureListener"); + gScreenCaptureListenerClassInfo.clazz = MakeGlobalRefOrDie(env, screenCaptureListenerClazz); + gScreenCaptureListenerClassInfo.onScreenCaptureComplete = + GetMethodIDOrDie(env, screenCaptureListenerClazz, "onScreenCaptureComplete", + "(Landroid/view/SurfaceControl$ScreenshotHardwareBuffer;)V"); return err; } |