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
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
@@ -610,6 +615,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}
|
||||
@@ -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 {
|
||||
@@ -2225,6 +2237,16 @@ public final class SurfaceControl implements Parcelable {
|
||||
return getPhysicalDisplayToken(physicalDisplayIds[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
@@ -2232,7 +2254,30 @@ public final class SurfaceControl implements Parcelable {
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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>
|
||||
@@ -186,6 +187,11 @@ static struct {
|
||||
jmethodID builder;
|
||||
} gScreenshotHardwareBufferClassInfo;
|
||||
|
||||
static struct {
|
||||
jclass clazz;
|
||||
jmethodID onScreenCaptureComplete;
|
||||
} gScreenCaptureListenerClassInfo;
|
||||
|
||||
static struct {
|
||||
jclass clazz;
|
||||
jmethodID ctor;
|
||||
@@ -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;
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
ScreenCaptureResults captureResults;
|
||||
status_t res = ScreenshotClient::captureDisplay(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::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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user