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

Commit f6a5990d authored by Patrick Williams's avatar Patrick Williams Committed by Android (Google) Code Review
Browse files

Merge "ScreenCaptureListenerWrapper - hold weak ref" into udc-dev

parents c8d3d7a5 20ca02e2
Loading
Loading
Loading
Loading
+6 −6
Original line number Diff line number Diff line
@@ -39,7 +39,6 @@ import android.os.ServiceManager;
import android.os.UserHandle;
import android.permission.IPermissionManager;
import android.util.Log;
import android.util.Pair;
import android.view.IWindowManager;
import android.view.InputDevice;
import android.view.InputEvent;
@@ -52,7 +51,8 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.IAccessibilityManager;
import android.window.ScreenCapture;
import android.window.ScreenCapture.CaptureArgs;
import android.window.ScreenCapture.ScreenCaptureListener;
import android.window.ScreenCapture.ScreenshotHardwareBuffer;
import android.window.ScreenCapture.SynchronousScreenCaptureListener;

import libcore.io.IoUtils;

@@ -235,12 +235,12 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
            final CaptureArgs captureArgs = new CaptureArgs.Builder<>()
                    .setSourceCrop(crop)
                    .build();
            Pair<ScreenCaptureListener, ScreenCapture.ScreenshotSync> syncScreenCapture =
            SynchronousScreenCaptureListener syncScreenCapture =
                    ScreenCapture.createSyncCaptureListener();
            mWindowManager.captureDisplay(DEFAULT_DISPLAY, captureArgs,
                    syncScreenCapture.first);
            final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
                    syncScreenCapture.second.get();
                    syncScreenCapture);
            final ScreenshotHardwareBuffer screenshotBuffer =
                    syncScreenCapture.getBuffer();
            return screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
        } catch (RemoteException re) {
            re.rethrowAsRuntimeException();
+38 −31
Original line number Diff line number Diff line
@@ -27,7 +27,6 @@ import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
import android.util.Pair;
import android.view.SurfaceControl;

import libcore.util.NativeAllocationRegistry;
@@ -73,14 +72,14 @@ public class ScreenCapture {
     */
    public static ScreenshotHardwareBuffer captureDisplay(
            DisplayCaptureArgs captureArgs) {
        Pair<ScreenCaptureListener, ScreenshotSync> syncScreenCapture = createSyncCaptureListener();
        int status = captureDisplay(captureArgs, syncScreenCapture.first);
        SynchronousScreenCaptureListener syncScreenCapture = createSyncCaptureListener();
        int status = captureDisplay(captureArgs, syncScreenCapture);
        if (status != 0) {
            return null;
        }

        try {
            return syncScreenCapture.second.get();
            return syncScreenCapture.getBuffer();
        } catch (Exception e) {
            return null;
        }
@@ -133,14 +132,14 @@ public class ScreenCapture {
     * @hide
     */
    public static ScreenshotHardwareBuffer captureLayers(LayerCaptureArgs captureArgs) {
        Pair<ScreenCaptureListener, ScreenshotSync> syncScreenCapture = createSyncCaptureListener();
        int status = captureLayers(captureArgs, syncScreenCapture.first);
        SynchronousScreenCaptureListener syncScreenCapture = createSyncCaptureListener();
        int status = captureLayers(captureArgs, syncScreenCapture);
        if (status != 0) {
            return null;
        }

        try {
            return syncScreenCapture.second.get();
            return syncScreenCapture.getBuffer();
        } catch (Exception e) {
            return null;
        }
@@ -743,14 +742,35 @@ public class ScreenCapture {
     * A helper method to handle the async screencapture callbacks synchronously. This should only
     * be used if the screencapture caller doesn't care that it blocks waiting for a screenshot.
     *
     * @return a Pair that holds the {@link ScreenCaptureListener} that should be used for capture
     * calls into SurfaceFlinger and a {@link ScreenshotSync} object to retrieve the results.
     */
    public static Pair<ScreenCaptureListener, ScreenshotSync> createSyncCaptureListener() {
        final ScreenshotSync screenshotSync = new ScreenshotSync();
        final ScreenCaptureListener screenCaptureListener = new ScreenCaptureListener(
                screenshotSync::setScreenshotHardwareBuffer);
        return new Pair<>(screenCaptureListener, screenshotSync);
     * @return a {@link SynchronousScreenCaptureListener} that should be used for capture
     * calls into SurfaceFlinger.
     */
    public static SynchronousScreenCaptureListener createSyncCaptureListener() {
        ScreenshotHardwareBuffer[] bufferRef = new ScreenshotHardwareBuffer[1];
        CountDownLatch latch = new CountDownLatch(1);
        Consumer<ScreenshotHardwareBuffer> consumer = buffer -> {
            bufferRef[0] = buffer;
            latch.countDown();
        };

        return new SynchronousScreenCaptureListener(consumer) {
            // In order to avoid requiring two GC cycles to clean up the consumer and the buffer
            // it references, the underlying JNI listener holds a weak reference to the consumer.
            // This property exists to ensure the consumer stays alive during the listener's
            // lifetime.
            private Consumer<ScreenshotHardwareBuffer> mConsumer = consumer;

            @Override
            public ScreenshotHardwareBuffer getBuffer() {
                try {
                    latch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS);
                    return bufferRef[0];
                } catch (Exception e) {
                    Log.e(TAG, "Failed to wait for screen capture result", e);
                    return null;
                }
            }
        };
    }

    /**
@@ -758,28 +778,15 @@ public class ScreenCapture {
     * {@link #captureLayers(LayerCaptureArgs, ScreenCaptureListener)} or
     * {@link #captureDisplay(DisplayCaptureArgs, ScreenCaptureListener)}
     */
    public static class ScreenshotSync {
        private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
        private ScreenshotHardwareBuffer mScreenshotHardwareBuffer;

        private void setScreenshotHardwareBuffer(
                ScreenshotHardwareBuffer screenshotHardwareBuffer) {
            mScreenshotHardwareBuffer = screenshotHardwareBuffer;
            mCountDownLatch.countDown();
    public abstract static class SynchronousScreenCaptureListener extends ScreenCaptureListener {
        SynchronousScreenCaptureListener(Consumer<ScreenshotHardwareBuffer> consumer) {
            super(consumer);
        }

        /**
         * Get the {@link ScreenshotHardwareBuffer} synchronously. This can be null if the
         * screenshot failed or if there was no callback in {@link #SCREENSHOT_WAIT_TIME_S} seconds.
         */
        public ScreenshotHardwareBuffer get() {
            try {
                mCountDownLatch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS);
                return mScreenshotHardwareBuffer;
            } catch (Exception e) {
                Log.e(TAG, "Failed to wait for screen capture result", e);
                return null;
            }
        }
        public abstract ScreenshotHardwareBuffer getBuffer();
    }
}
+14 −8
Original line number Diff line number Diff line
@@ -81,22 +81,28 @@ class ScreenCaptureListenerWrapper : public gui::BnScreenCaptureListener {
public:
    explicit ScreenCaptureListenerWrapper(JNIEnv* env, jobject jobject) {
        env->GetJavaVM(&mVm);
        mConsumerObject = env->NewGlobalRef(jobject);
        LOG_ALWAYS_FATAL_IF(!mConsumerObject, "Failed to make global ref");
        mConsumerWeak = env->NewWeakGlobalRef(jobject);
    }

    ~ScreenCaptureListenerWrapper() {
        if (mConsumerObject) {
            getenv()->DeleteGlobalRef(mConsumerObject);
            mConsumerObject = nullptr;
        if (mConsumerWeak) {
            getenv()->DeleteWeakGlobalRef(mConsumerWeak);
            mConsumerWeak = nullptr;
        }
    }

    binder::Status onScreenCaptureCompleted(
            const gui::ScreenCaptureResults& captureResults) override {
        JNIEnv* env = getenv();

        ScopedLocalRef<jobject> consumer{env, env->NewLocalRef(mConsumerWeak)};
        if (consumer == nullptr) {
            ALOGE("ScreenCaptureListenerWrapper consumer not alive.");
            return binder::Status::ok();
        }

        if (!captureResults.fenceResult.ok() || captureResults.buffer == nullptr) {
            env->CallVoidMethod(mConsumerObject, gConsumerClassInfo.accept, nullptr);
            env->CallVoidMethod(consumer.get(), gConsumerClassInfo.accept, nullptr);
            checkAndClearException(env, "accept");
            return binder::Status::ok();
        }
@@ -111,7 +117,7 @@ public:
                                            captureResults.capturedSecureLayers,
                                            captureResults.capturedHdrLayers);
        checkAndClearException(env, "builder");
        env->CallVoidMethod(mConsumerObject, gConsumerClassInfo.accept, screenshotHardwareBuffer);
        env->CallVoidMethod(consumer.get(), gConsumerClassInfo.accept, screenshotHardwareBuffer);
        checkAndClearException(env, "accept");
        env->DeleteLocalRef(jhardwareBuffer);
        env->DeleteLocalRef(screenshotHardwareBuffer);
@@ -119,7 +125,7 @@ public:
    }

private:
    jobject mConsumerObject;
    jweak mConsumerWeak;
    JavaVM* mVm;

    JNIEnv* getenv() {
+8 −8
Original line number Diff line number Diff line
@@ -80,8 +80,7 @@ import android.view.ViewRootImpl;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.window.ScreenCapture;
import android.window.ScreenCapture.ScreenCaptureListener;
import android.window.ScreenCapture.ScreenshotSync;
import android.window.ScreenCapture.SynchronousScreenCaptureListener;

import androidx.annotation.MainThread;
import androidx.annotation.Nullable;
@@ -1222,10 +1221,11 @@ public class BubbleController implements ConfigurationChangeListener,

    /**
     * Performs a screenshot that may exclude the bubble layer, if one is present. The screenshot
     * can be access via the supplied {@link ScreenshotSync#get()} asynchronously.
     * can be access via the supplied {@link SynchronousScreenCaptureListener#getBuffer()}
     * asynchronously.
     */
    public void getScreenshotExcludingBubble(int displayId,
            Pair<ScreenCaptureListener, ScreenshotSync> screenCaptureListener) {
            SynchronousScreenCaptureListener screenCaptureListener) {
        try {
            ScreenCapture.CaptureArgs args = null;
            if (mStackView != null) {
@@ -1240,7 +1240,7 @@ public class BubbleController implements ConfigurationChangeListener,
                }
            }

            mWmService.captureDisplay(displayId, args, screenCaptureListener.first);
            mWmService.captureDisplay(displayId, args, screenCaptureListener);
        } catch (RemoteException e) {
            Log.e(TAG, "Failed to capture screenshot");
        }
@@ -2211,15 +2211,15 @@ public class BubbleController implements ConfigurationChangeListener,

        @Override
        @Nullable
        public ScreenshotSync getScreenshotExcludingBubble(int displayId) {
            Pair<ScreenCaptureListener, ScreenshotSync> screenCaptureListener =
        public SynchronousScreenCaptureListener getScreenshotExcludingBubble(int displayId) {
            SynchronousScreenCaptureListener screenCaptureListener =
                    ScreenCapture.createSyncCaptureListener();

            mMainExecutor.execute(
                    () -> BubbleController.this.getScreenshotExcludingBubble(displayId,
                            screenCaptureListener));

            return screenCaptureListener.second;
            return screenCaptureListener;
        }

        @Override
+8 −8
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

package com.android.wm.shell.bubbles;

import static android.window.ScreenCapture.ScreenshotSync;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
import static java.lang.annotation.ElementType.PARAMETER;
@@ -34,6 +32,7 @@ import android.service.notification.NotificationListenerService.RankingMap;
import android.util.Pair;
import android.util.SparseArray;
import android.window.ScreenCapture.ScreenshotHardwareBuffer;
import android.window.ScreenCapture.SynchronousScreenCaptureListener;

import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
@@ -150,13 +149,14 @@ public interface Bubbles {
    boolean isAppBubbleTaskId(int taskId);

    /**
     * @return a {@link ScreenshotSync} after performing a screenshot that may exclude the bubble
     * layer, if one is present. The underlying {@link ScreenshotHardwareBuffer} can be access via
     * {@link ScreenshotSync#get()} asynchronously and care should be taken to
     * {@link HardwareBuffer#close()} the associated
     * {@link ScreenshotHardwareBuffer#getHardwareBuffer()} when no longer required.
`    * @return a {@link SynchronousScreenCaptureListener} after performing a screenshot that may
     * exclude the bubble layer, if one is present. The underlying
     * {@link ScreenshotHardwareBuffer} can be accessed via
     * {@link SynchronousScreenCaptureListener#getBuffer()} asynchronously and care should be taken
     * to {@link HardwareBuffer#close()} the associated
     * {@link ScreenshotHardwareBuffer#getHardwareBuffer()} when no longer required.`
     */
    ScreenshotSync getScreenshotExcludingBubble(int displayId);
    SynchronousScreenCaptureListener getScreenshotExcludingBubble(int displayId);

    /**
     * @return a bubble that matches the provided shortcutId, if one exists.
Loading