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:
chaviw
2020-08-18 16:06:40 -07:00
parent 25953d498f
commit 3510924a6d
3 changed files with 172 additions and 46 deletions

View File

@@ -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;

View File

@@ -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);
}
/**

View File

@@ -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;
}