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

Commit 6ab3ffa7 authored by Mark Renouf's avatar Mark Renouf
Browse files

Use OnImageAvailableListener to wait for buffer

This eliminates a dependency on having the client wait until a buffer
is produced before invoking the callback.

Bug: 194927650
Test: atest ScrollCaptureClientTest
Change-Id: I9b479217e23b8a7558ecc046a2d5c41156c22baa
parent bad6f056
Loading
Loading
Loading
Loading
+46 −8
Original line number Diff line number Diff line
@@ -45,9 +45,12 @@ import androidx.concurrent.futures.CallbackToFutureAdapter;
import androidx.concurrent.futures.CallbackToFutureAdapter.Completer;

import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.qualifiers.Background;

import com.google.common.util.concurrent.ListenableFuture;

import java.util.concurrent.Executor;

import javax.inject.Inject;

/**
@@ -63,6 +66,8 @@ public class ScrollCaptureClient {

    private static final String TAG = LogConfig.logTag(ScrollCaptureClient.class);

    private final Executor mBgExecutor;

    /**
     * Represents the connection to a target window and provides a mechanism for requesting tiles.
     */
@@ -155,8 +160,10 @@ public class ScrollCaptureClient {
    private IBinder mHostWindowToken;

    @Inject
    public ScrollCaptureClient(@UiContext Context context, IWindowManager windowManagerService) {
    public ScrollCaptureClient(IWindowManager windowManagerService,
            @Background Executor bgExecutor, @UiContext Context context) {
        requireNonNull(context.getDisplay(), "context must be associated with a Display!");
        mBgExecutor = bgExecutor;
        mWindowManagerService = windowManagerService;
    }

@@ -220,21 +227,25 @@ public class ScrollCaptureClient {
                return "";
            }
            SessionWrapper session = new SessionWrapper(connection, response.getWindowBounds(),
                    response.getBoundsInWindow(), maxPages);
                    response.getBoundsInWindow(), maxPages, mBgExecutor);
            session.start(completer);
            return "IScrollCaptureCallbacks#onCaptureStarted";
        });
    }

    private static class SessionWrapper extends IScrollCaptureCallbacks.Stub implements Session,
            IBinder.DeathRecipient {
            IBinder.DeathRecipient, ImageReader.OnImageAvailableListener {

        private IScrollCaptureConnection mConnection;
        private final Executor mBgExecutor;
        private final Object mLock = new Object();

        private ImageReader mReader;
        private final int mTileHeight;
        private final int mTileWidth;
        private Rect mRequestRect;
        private Rect mCapturedArea;
        private Image mCapturedImage;
        private boolean mStarted;
        private final int mTargetHeight;

@@ -247,7 +258,8 @@ public class ScrollCaptureClient {
        private Completer<Void> mEndCompleter;

        private SessionWrapper(IScrollCaptureConnection connection, Rect windowBounds,
                Rect boundsInWindow, float maxPages) throws RemoteException {
                Rect boundsInWindow, float maxPages, Executor bgExecutor)
                throws RemoteException {
            mConnection = requireNonNull(connection);
            mConnection.asBinder().linkToDeath(SessionWrapper.this, 0);
            mWindowBounds = requireNonNull(windowBounds);
@@ -259,7 +271,7 @@ public class ScrollCaptureClient {
            mTileWidth = mBoundsInWindow.width();
            mTileHeight = pxPerTile / mBoundsInWindow.width();
            mTargetHeight = (int) (mBoundsInWindow.height() * maxPages);

            mBgExecutor = bgExecutor;
            if (DEBUG_SCROLL) {
                Log.d(TAG, "boundsInWindow: " + mBoundsInWindow);
                Log.d(TAG, "tile size: " + mTileWidth + "x" + mTileHeight);
@@ -289,6 +301,7 @@ public class ScrollCaptureClient {
            mReader = ImageReader.newInstance(mTileWidth, mTileHeight, PixelFormat.RGBA_8888,
                    MAX_TILES, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
            mStartCompleter = completer;
            mReader.setOnImageAvailableListenerWithExecutor(this, mBgExecutor);
            try {
                mCancellationSignal = mConnection.startCapture(mReader.getSurface(), this);
                completer.addCancellationListener(() -> {
@@ -339,9 +352,34 @@ public class ScrollCaptureClient {

        @BinderThread
        @Override
        public void onImageRequestCompleted(int flags, Rect contentArea) {
            Image image = mReader.acquireLatestImage();
            mTileRequestCompleter.set(new CaptureResult(image, mRequestRect, contentArea));
        public void onImageRequestCompleted(int flagsUnused, Rect contentArea) {
            synchronized (mLock) {
                mCapturedArea = contentArea;
                if (mCapturedImage != null || (mCapturedArea == null || mCapturedArea.isEmpty())) {
                    completeCaptureRequest();
                }
            }
        }

        /** @see ImageReader.OnImageAvailableListener */
        @Override
        public void onImageAvailable(ImageReader reader) {
            synchronized (mLock) {
                mCapturedImage = mReader.acquireLatestImage();
                if (mCapturedArea != null) {
                    completeCaptureRequest();
                }
            }
        }

        /** Produces a result for the caller as soon as both asynchronous results are received. */
        private void completeCaptureRequest() {
            CaptureResult result =
                    new CaptureResult(mCapturedImage, mRequestRect, mCapturedArea);
            mCapturedImage = null;
            mRequestRect = null;
            mCapturedArea = null;
            mTileRequestCompleter.set(result);
        }

        @Override
+1 −3
Original line number Diff line number Diff line
@@ -26,7 +26,6 @@ import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;

import android.content.Context;
import android.graphics.Rect;
import android.os.RemoteException;
import android.testing.AndroidTestingRunner;
@@ -36,7 +35,6 @@ import android.view.IWindowManager;
import android.view.ScrollCaptureResponse;

import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;

import com.android.systemui.SysuiTestCase;
import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult;
@@ -83,7 +81,7 @@ public class ScrollCaptureClientTest extends SysuiTestCase {
                /* taskId */ anyInt(), any(IScrollCaptureResponseListener.class));

        // Create client
        ScrollCaptureClient client = new ScrollCaptureClient(mContext, mWm);
        ScrollCaptureClient client = new ScrollCaptureClient(mWm, Runnable::run, mContext);

        // Request scroll capture
        ListenableFuture<ScrollCaptureResponse> requestFuture =