Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3510924a authored by chaviw's avatar chaviw
Browse files

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
parent 25953d49
Loading
Loading
Loading
Loading
+9 −3
Original line number Diff line number Diff line
@@ -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;

+87 −9
Original line number Diff line number Diff line
@@ -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);
    }

    /**
+76 −34
Original line number Diff line number Diff line
@@ -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;
    }

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