summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorchaviw <chaviw@google.com>2020-08-18 16:06:40 -0700
committerchaviw <chaviw@google.com>2020-09-08 09:59:50 -0700
commit3510924a6de6ad44160de44051d28df87593edd3 (patch)
tree1d547c3c90b165a509d26a7f9deeb70783224791
parent25953d498f68ac3cdc3b7f4c0a33946725ac960d (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.cpp12
-rw-r--r--core/java/android/view/SurfaceControl.java96
-rw-r--r--core/jni/android_view_SurfaceControl.cpp110
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;
}