public final class

SurfaceRequest

extends java.lang.Object

 java.lang.Object

↳androidx.camera.core.SurfaceRequest

Gradle dependencies

compile group: 'androidx.camera', name: 'camera-core', version: '1.2.0-alpha01'

  • groupId: androidx.camera
  • artifactId: camera-core
  • version: 1.2.0-alpha01

Artifact androidx.camera:camera-core:1.2.0-alpha01 it located at Google repository (https://maven.google.com/)

Overview

A completable, single-use request of a .

Contains requirements for surface characteristics along with methods for completing the request and listening for request cancellation.

Summary

Constructors
publicSurfaceRequest(Size resolution, CameraInternal camera, boolean isRGBA8888Required)

Creates a new surface request with the given resolution, the Camera and the crop rect.

Methods
public voidaddRequestCancellationListener(java.util.concurrent.Executor executor, java.lang.Runnable listener)

Adds a listener to be informed when the camera cancels the surface request.

public voidclearTransformationInfoListener()

Clears the SurfaceRequest.TransformationInfoListener set via SurfaceRequest.setTransformationInfoListener(Executor, SurfaceRequest.TransformationInfoListener).

public CameraInternalgetCamera()

Returns the Camera which is requesting a .

public DeferrableSurfacegetDeferrableSurface()

Returns the DeferrableSurface instance used to track usage of the surface that fulfills this request.

public SizegetResolution()

Returns the resolution of the requested .

public booleanisRGBA8888Required()

Returns whether a surface of RGBA_8888 pixel format is required.

public voidprovideSurface(Surface surface, java.util.concurrent.Executor executor, Consumer<SurfaceRequest.Result> resultListener)

Completes the request for a if it has not already been completed or cancelled.

public voidsetTransformationInfoListener(java.util.concurrent.Executor executor, SurfaceRequest.TransformationInfoListener listener)

Sets a listener to receive updates on transformation info.

public voidupdateTransformationInfo(SurfaceRequest.TransformationInfo transformationInfo)

Updates the SurfaceRequest.TransformationInfo associated with this SurfaceRequest.

public booleanwillNotProvideSurface()

Signals that the request will never be fulfilled.

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructors

public SurfaceRequest(Size resolution, CameraInternal camera, boolean isRGBA8888Required)

Creates a new surface request with the given resolution, the Camera and the crop rect.

Methods

public DeferrableSurface getDeferrableSurface()

Returns the DeferrableSurface instance used to track usage of the surface that fulfills this request.

public Size getResolution()

Returns the resolution of the requested . The surface which fulfills this request must have the resolution specified here in order to fulfill the resource requirements of the camera. Fulfillment of the request with a surface of a different resolution may cause the resultListener passed to SurfaceRequest.provideSurface(Surface, Executor, Consumer) to be invoked with a SurfaceRequest.Result containing SurfaceRequest.Result.RESULT_INVALID_SURFACE.

Returns:

The guaranteed supported resolution.

See also: SurfaceTexture

public CameraInternal getCamera()

Returns the Camera which is requesting a .

public boolean isRGBA8888Required()

Returns whether a surface of RGBA_8888 pixel format is required.

Returns:

true if a surface of RGBA_8888 pixel format is required.

public void provideSurface(Surface surface, java.util.concurrent.Executor executor, Consumer<SurfaceRequest.Result> resultListener)

Completes the request for a if it has not already been completed or cancelled.

Once the camera no longer needs the provided surface, the resultListener will be invoked with a SurfaceRequest.Result containing SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY. At this point it is safe to release the surface and any underlying resources. Releasing the surface before receiving this signal may cause undesired behavior on lower API levels.

If the request is cancelled by the camera before successfully attaching the provided surface to the camera, then the resultListener will be invoked with a SurfaceRequest.Result containing SurfaceRequest.Result.RESULT_REQUEST_CANCELLED. In addition, any cancellation listeners provided to SurfaceRequest.addRequestCancellationListener(Executor, Runnable) will be invoked.

If the request was previously completed via SurfaceRequest.willNotProvideSurface(), then resultListener will be invoked with a SurfaceRequest.Result containing SurfaceRequest.Result.RESULT_WILL_NOT_PROVIDE_SURFACE.

Upon returning from this method, the surface request is guaranteed to be complete. However, only the resultListener provided to the first invocation of this method should be used to track when the provided is no longer in use by the camera, as subsequent invocations will always invoke the resultListener with a SurfaceRequest.Result containing SurfaceRequest.Result.RESULT_SURFACE_ALREADY_PROVIDED.

Parameters:

surface: The surface which will complete the request.
executor: Executor used to execute the resultListener.
resultListener: Listener used to track how the surface is used by the camera in response to being provided by this method.

public boolean willNotProvideSurface()

Signals that the request will never be fulfilled.

This may be called in the case where the application may be shutting down and a surface will never be produced to fulfill the request.

This should always be called as soon as it is known that the request will not be fulfilled. Failure to complete the SurfaceRequest via willNotProvideSurface() or SurfaceRequest.provideSurface(Surface, Executor, Consumer) may cause long delays in shutting down the camera.

Upon returning from this method, the request is guaranteed to be complete, regardless of the return value. If the request was previously successfully completed by SurfaceRequest.provideSurface(Surface, Executor, Consumer), invoking this method will return false, and will have no effect on how the surface is used by the camera.

Returns:

true if this call to willNotProvideSurface() successfully completes the request, i.e., the request has not already been completed via SurfaceRequest.provideSurface(Surface, Executor, Consumer) or by a previous call to willNotProvideSurface() and has not already been cancelled by the camera.

public void addRequestCancellationListener(java.util.concurrent.Executor executor, java.lang.Runnable listener)

Adds a listener to be informed when the camera cancels the surface request.

A surface request may be cancelled by the camera if the surface is no longer required. Examples of why cancellation may occur include (1) a UseCase being unbound from the camera, (2) surface requirements, such as resolution, changing due to newly bound use cases, or (3) the camera requiring a new object on lower API levels to work around compatibility issues.

When a request is cancelled, the Runnables provided here will be invoked on the java.util.concurrent.Executor they are added with, and can be used as a signal to stop any work that may be in progress to fulfill the surface request.

Once a surface request has been cancelled by the camera, SurfaceRequest.willNotProvideSurface() will have no effect and will return false. Attempting to complete the request via SurfaceRequest.provideSurface(Surface, Executor, Consumer) will also have no effect, and any resultListener passed to SurfaceRequest.provideSurface(Surface, Executor, Consumer) will be invoked with a SurfaceRequest.Result containing SurfaceRequest.Result.RESULT_REQUEST_CANCELLED.

Note that due to the asynchronous nature of this listener, it is not guaranteed that the listener will be called before an attempt to complete the request with SurfaceRequest.provideSurface(Surface, Executor, Consumer) or SurfaceRequest.willNotProvideSurface(), so the return values of these methods can always be checked if your workflow for producing a surface expects them to complete successfully.

Parameters:

executor: The executor used to notify the listener.
listener: The listener which will be run upon request cancellation.

public void updateTransformationInfo(SurfaceRequest.TransformationInfo transformationInfo)

Updates the SurfaceRequest.TransformationInfo associated with this SurfaceRequest.

public void setTransformationInfoListener(java.util.concurrent.Executor executor, SurfaceRequest.TransformationInfoListener listener)

Sets a listener to receive updates on transformation info.

Sets a listener to receive the transformation info associated with this SurfaceRequest when it changes or becomes available. The listener is called immediately if transformation info is available at the time of setting.

Parameters:

executor: The executor used to notify the listener.
listener: the listener which will be called when transformation info changes.

See also: SurfaceRequest.TransformationInfoListener, SurfaceRequest.TransformationInfo

public void clearTransformationInfoListener()

Clears the SurfaceRequest.TransformationInfoListener set via SurfaceRequest.setTransformationInfoListener(Executor, SurfaceRequest.TransformationInfoListener).

Source

/*
 * Copyright 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.camera.core;

import android.annotation.SuppressLint;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraCharacteristics;
import android.util.Size;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceView;
import android.view.TextureView;

import androidx.annotation.GuardedBy;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.camera.core.impl.CameraInternal;
import androidx.camera.core.impl.DeferrableSurface;
import androidx.camera.core.impl.ImageFormatConstants;
import androidx.camera.core.impl.ImageOutputConfig;
import androidx.camera.core.impl.utils.executor.CameraXExecutors;
import androidx.camera.core.impl.utils.futures.FutureCallback;
import androidx.camera.core.impl.utils.futures.Futures;
import androidx.concurrent.futures.CallbackToFutureAdapter;
import androidx.core.util.Consumer;
import androidx.core.util.Preconditions;

import com.google.auto.value.AutoValue;
import com.google.common.util.concurrent.ListenableFuture;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;

/**
 * A completable, single-use request of a {@link Surface}.
 *
 * <p>Contains requirements for surface characteristics along with methods for completing the
 * request and listening for request cancellation.
 *
 * @see Preview.SurfaceProvider#onSurfaceRequested(SurfaceRequest)
 */
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
public final class SurfaceRequest {
    private final Object mLock = new Object();

    private final Size mResolution;
    private final boolean mRGBA8888Required;

    private final CameraInternal mCamera;

    // For the camera to retrieve the surface from the user
    @SuppressWarnings("WeakerAccess") /*synthetic accessor */
    final ListenableFuture<Surface> mSurfaceFuture;
    private final CallbackToFutureAdapter.Completer<Surface> mSurfaceCompleter;

    // For the user to wait for the camera to be finished with the surface and retrieve errors
    // from the camera.
    private final ListenableFuture<Void> mSessionStatusFuture;

    // For notification of surface request cancellation. Should only be used to register
    // cancellation listeners.
    private final CallbackToFutureAdapter.Completer<Void> mRequestCancellationCompleter;

    private final DeferrableSurface mInternalDeferrableSurface;

    @GuardedBy("mLock")
    @Nullable
    private TransformationInfo mTransformationInfo;
    @GuardedBy("mLock")
    @Nullable
    private TransformationInfoListener mTransformationInfoListener;
    // Executor for calling TransformationUpdateListener.
    @GuardedBy("mLock")
    @Nullable
    private Executor mTransformationInfoExecutor;

    /**
     * Creates a new surface request with the given resolution, the {@link Camera} and the crop
     * rect.
     *
     * @hide
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public SurfaceRequest(
            @NonNull Size resolution,
            @NonNull CameraInternal camera,
            boolean isRGBA8888Required) {
        super();
        mResolution = resolution;
        mCamera = camera;
        mRGBA8888Required = isRGBA8888Required;

        // To ensure concurrency and ordering, operations are chained. Completion can only be
        // triggered externally by the top-level completer (mSurfaceCompleter). The other future
        // completers are only completed by callbacks set up within the constructor of this class
        // to ensure correct ordering of events.

        // Cancellation listener must be called last to ensure the result can be retrieved from
        // the session listener.
        String surfaceRequestString =
                "SurfaceRequest[size: " + resolution + ", id: " + this.hashCode() + "]";
        AtomicReference<CallbackToFutureAdapter.Completer<Void>> cancellationCompleterRef =
                new AtomicReference<>(null);
        ListenableFuture<Void> requestCancellationFuture =
                CallbackToFutureAdapter.getFuture(completer -> {
                    cancellationCompleterRef.set(completer);
                    return surfaceRequestString + "-cancellation";
                });
        CallbackToFutureAdapter.Completer<Void> requestCancellationCompleter =
                Preconditions.checkNotNull(cancellationCompleterRef.get());
        mRequestCancellationCompleter = requestCancellationCompleter;

        // Surface session status future completes and is responsible for finishing the
        // cancellation listener.
        AtomicReference<CallbackToFutureAdapter.Completer<Void>> sessionStatusCompleterRef =
                new AtomicReference<>(null);
        mSessionStatusFuture = CallbackToFutureAdapter.getFuture(completer -> {
            sessionStatusCompleterRef.set(completer);
            return surfaceRequestString + "-status";
        });

        Futures.addCallback(mSessionStatusFuture, new FutureCallback<Void>() {
            @Override
            public void onSuccess(@Nullable Void result) {
                // Cancellation didn't occur, so complete the cancellation future. There
                // shouldn't ever be any standard listeners on this future, so nothing should be
                // invoked.
                Preconditions.checkState(requestCancellationCompleter.set(null));
            }

            @Override
            public void onFailure(Throwable t) {
                if (t instanceof RequestCancelledException) {
                    // Cancellation occurred. Notify listeners.
                    Preconditions.checkState(requestCancellationFuture.cancel(false));
                } else {
                    // Cancellation didn't occur, complete the future so cancellation listeners
                    // are not invoked.
                    Preconditions.checkState(requestCancellationCompleter.set(null));
                }
            }
        }, CameraXExecutors.directExecutor());

        // Create the surface future/completer. This will be used to complete the rest of the
        // future chain and can be set externally via SurfaceRequest methods.
        CallbackToFutureAdapter.Completer<Void> sessionStatusCompleter =
                Preconditions.checkNotNull(sessionStatusCompleterRef.get());
        AtomicReference<CallbackToFutureAdapter.Completer<Surface>> surfaceCompleterRef =
                new AtomicReference<>(null);
        mSurfaceFuture = CallbackToFutureAdapter.getFuture(completer -> {
            surfaceCompleterRef.set(completer);
            return surfaceRequestString + "-Surface";
        });
        mSurfaceCompleter = Preconditions.checkNotNull(surfaceCompleterRef.get());

        // Create the deferrable surface which will be used for communicating when the
        // camera and consumer are done using the surface. Note this anonymous inner class holds
        // an implicit reference to the SurfaceRequest. This is by design, and ensures the
        // SurfaceRequest and all contained future completers will not be garbage collected as
        // long as the DeferrableSurface is referenced externally (via getDeferrableSurface()).
        mInternalDeferrableSurface = new DeferrableSurface(resolution,
                ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE) {
            @NonNull
            @Override
            protected ListenableFuture<Surface> provideSurface() {
                return mSurfaceFuture;
            }
        };
        ListenableFuture<Void> terminationFuture =
                mInternalDeferrableSurface.getTerminationFuture();

        // Propagate surface completion to the session future.
        Futures.addCallback(mSurfaceFuture, new FutureCallback<Surface>() {
            @Override
            public void onSuccess(@Nullable Surface result) {
                // On successful setting of a surface, defer completion of the session future to
                // the DeferrableSurface termination future. Once that future completes, then it
                // is safe to release the Surface and associated resources.
                Futures.propagate(terminationFuture, sessionStatusCompleter);
            }

            @Override
            public void onFailure(Throwable t) {
                // Translate cancellation into a SurfaceRequestCancelledException. Other
                // exceptions mean either the request was completed via willNotProvideSurface() or a
                // programming error occurred. In either case, the user will never see the
                // session future (an immediate future will be returned instead), so complete the
                // future so cancellation listeners are never called.
                if (t instanceof CancellationException) {
                    Preconditions.checkState(sessionStatusCompleter.setException(
                            new RequestCancelledException(
                                    surfaceRequestString + " cancelled.", t)));
                } else {
                    sessionStatusCompleter.set(null);
                }
            }
        }, CameraXExecutors.directExecutor());

        // If the deferrable surface is terminated, there are two cases:
        // 1. The surface has not yet been provided to the camera (or marked as 'will not
        //    complete'). Treat this as if the surface request has been cancelled.
        // 2. The surface was already provided to the camera. In this case the camera is now
        //    finished with the surface, so cancelling the surface future below will be a no-op.
        terminationFuture.addListener(() -> mSurfaceFuture.cancel(true),
                CameraXExecutors.directExecutor());
    }

    /**
     * Returns the {@link DeferrableSurface} instance used to track usage of the surface that
     * fulfills this request.
     *
     * @hide
     */
    @NonNull
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public DeferrableSurface getDeferrableSurface() {
        return mInternalDeferrableSurface;
    }

    /**
     * Returns the resolution of the requested {@link Surface}.
     *
     * The surface which fulfills this request must have the resolution specified here in
     * order to fulfill the resource requirements of the camera. Fulfillment of the request
     * with a surface of a different resolution may cause the {@code resultListener} passed to
     * {@link #provideSurface(Surface, Executor, Consumer)} to be invoked with a {@link Result}
     * containing {@link Result#RESULT_INVALID_SURFACE}.
     *
     * @return The guaranteed supported resolution.
     * @see SurfaceTexture#setDefaultBufferSize(int, int)
     */
    @NonNull
    public Size getResolution() {
        return mResolution;
    }

    /**
     * Returns the {@link Camera} which is requesting a {@link Surface}.
     *
     * @hide
     */
    @NonNull
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public CameraInternal getCamera() {
        return mCamera;
    }

    /**
     * Returns whether a surface of RGBA_8888 pixel format is required.
     *
     * @return true if a surface of RGBA_8888 pixel format is required.
     * @hide
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public boolean isRGBA8888Required() {
        return mRGBA8888Required;
    }

    /**
     * Completes the request for a {@link Surface} if it has not already been
     * completed or cancelled.
     *
     * <p>Once the camera no longer needs the provided surface, the {@code resultListener} will be
     * invoked with a {@link Result} containing {@link Result#RESULT_SURFACE_USED_SUCCESSFULLY}.
     * At this point it is safe to release the surface and any underlying resources. Releasing
     * the surface before receiving this signal may cause undesired behavior on lower API levels.
     *
     * <p>If the request is cancelled by the camera before successfully attaching the
     * provided surface to the camera, then the {@code resultListener} will be invoked with a
     * {@link Result} containing {@link Result#RESULT_REQUEST_CANCELLED}. In addition, any
     * cancellation listeners provided to
     * {@link #addRequestCancellationListener(Executor, Runnable)} will be invoked.
     *
     * <p>If the request was previously completed via {@link #willNotProvideSurface()}, then
     * {@code resultListener} will be invoked with a {@link Result} containing
     * {@link Result#RESULT_WILL_NOT_PROVIDE_SURFACE}.
     *
     * <p>Upon returning from this method, the surface request is guaranteed to be complete.
     * However, only the {@code resultListener} provided to the first invocation of this method
     * should be used to track when the provided {@link Surface} is no longer in use by the
     * camera, as subsequent invocations will always invoke the {@code resultListener} with a
     * {@link Result} containing {@link Result#RESULT_SURFACE_ALREADY_PROVIDED}.
     *
     * @param surface        The surface which will complete the request.
     * @param executor       Executor used to execute the {@code resultListener}.
     * @param resultListener Listener used to track how the surface is used by the camera in
     *                       response to being provided by this method.
     */
    public void provideSurface(@NonNull Surface surface, @NonNull Executor executor,
            @NonNull Consumer<Result> resultListener) {
        if (mSurfaceCompleter.set(surface) || mSurfaceFuture.isCancelled()) {
            // Session will be pending completion (or surface request was cancelled). Return the
            // session future.
            Futures.addCallback(mSessionStatusFuture, new FutureCallback<Void>() {
                @Override
                public void onSuccess(@Nullable Void result) {
                    resultListener.accept(Result.of(Result.RESULT_SURFACE_USED_SUCCESSFULLY,
                            surface));
                }

                @Override
                public void onFailure(Throwable t) {
                    Preconditions.checkState(t instanceof RequestCancelledException, "Camera "
                            + "surface session should only fail with request "
                            + "cancellation. Instead failed due to:\n" + t);
                    resultListener.accept(Result.of(Result.RESULT_REQUEST_CANCELLED, surface));
                }
            }, executor);
        } else {
            // Surface request is already complete
            Preconditions.checkState(mSurfaceFuture.isDone());
            try {
                mSurfaceFuture.get();
                // Getting this far means the surface was already provided.
                executor.execute(
                        () -> resultListener.accept(
                                Result.of(Result.RESULT_SURFACE_ALREADY_PROVIDED, surface)));
            } catch (InterruptedException | ExecutionException e) {
                executor.execute(
                        () -> resultListener.accept(
                                Result.of(Result.RESULT_WILL_NOT_PROVIDE_SURFACE, surface)));
            }

        }
    }

    /**
     * Signals that the request will never be fulfilled.
     *
     * <p>This may be called in the case where the application may be shutting down and a
     * surface will never be produced to fulfill the request.
     *
     * <p>This should always be called as soon as it is known that the request will not
     * be fulfilled. Failure to complete the SurfaceRequest via {@code willNotProvideSurface()}
     * or {@link #provideSurface(Surface, Executor, Consumer)} may cause long delays in shutting
     * down the camera.
     *
     * <p>Upon returning from this method, the request is guaranteed to be complete, regardless
     * of the return value. If the request was previously successfully completed by
     * {@link #provideSurface(Surface, Executor, Consumer)}, invoking this method will return
     * {@code false}, and will have no effect on how the surface is used by the camera.
     *
     * @return {@code true} if this call to {@code willNotProvideSurface()} successfully
     * completes the request, i.e., the request has not already been completed via
     * {@link #provideSurface(Surface, Executor, Consumer)} or by a previous call to
     * {@code willNotProvideSurface()} and has not already been cancelled by the camera.
     */
    public boolean willNotProvideSurface() {
        return mSurfaceCompleter.setException(
                new DeferrableSurface.SurfaceUnavailableException("Surface request "
                        + "will not complete."));
    }

    /**
     * Adds a listener to be informed when the camera cancels the surface request.
     *
     * <p>A surface request may be cancelled by the camera if the surface is no longer required.
     * Examples of why cancellation may occur include (1) a {@link UseCase} being unbound from the
     * camera, (2) surface requirements, such as {@linkplain #getResolution() resolution}, changing
     * due to newly bound use cases, or (3) the camera requiring a new {@link Surface}
     * object on lower API levels to work around compatibility issues.
     *
     * <p>When a request is cancelled, the {@link Runnable Runnables} provided here will be
     * invoked on the {@link Executor} they are added with, and can be used as a signal to stop any
     * work that may be in progress to fulfill the surface request.
     *
     * <p>Once a surface request has been cancelled by the camera,
     * {@link #willNotProvideSurface()} will have no effect and will return {@code false}.
     * Attempting to complete the request via {@link #provideSurface(Surface, Executor, Consumer)}
     * will also have no effect, and any {@code resultListener} passed to
     * {@link #provideSurface(Surface, Executor, Consumer)} will be invoked with a {@link Result}
     * containing {@link Result#RESULT_REQUEST_CANCELLED}.
     *
     * <p>Note that due to the asynchronous nature of this listener, it is not guaranteed
     * that the listener will be called before an attempt to complete the request with
     * {@link #provideSurface(Surface, Executor, Consumer)} or {@link #willNotProvideSurface()}, so
     * the return values of these methods can always be checked if your workflow for producing a
     * surface expects them to complete successfully.
     *
     * @param executor The executor used to notify the listener.
     * @param listener The listener which will be run upon request cancellation.
     */
    // Since registered listeners will only be called once, and the lifetime of a SurfaceRequest
    // is relatively short, there is no need for a 'removeRequestCancellationListener()' method.
    // References to listeners are also automatically freed up upon completion of the request due
    // to the implementation of CallbackToFutureAdapter.Completer.
    @SuppressLint("PairedRegistration")
    public void addRequestCancellationListener(@NonNull Executor executor,
            @NonNull Runnable listener) {
        mRequestCancellationCompleter.addCancellationListener(listener, executor);
    }

    /**
     * Updates the {@link TransformationInfo} associated with this {@link SurfaceRequest}.
     *
     * @hide
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public void updateTransformationInfo(@NonNull TransformationInfo transformationInfo) {
        TransformationInfoListener listener;
        Executor executor;
        synchronized (mLock) {
            mTransformationInfo = transformationInfo;
            listener = mTransformationInfoListener;
            executor = mTransformationInfoExecutor;
        }
        if (listener != null && executor != null) {
            executor.execute(() -> listener.onTransformationInfoUpdate(transformationInfo));
        }
    }

    /**
     * Sets a listener to receive updates on transformation info.
     *
     * <p> Sets a listener to receive the transformation info associated with this
     * {@link SurfaceRequest} when it changes or becomes available. The listener is called
     * immediately if transformation info is available at the time of setting.
     *
     * @param executor The executor used to notify the listener.
     * @param listener the listener which will be called when transformation info changes.
     * @see TransformationInfoListener
     * @see TransformationInfo
     */
    public void setTransformationInfoListener(@NonNull Executor executor,
            @NonNull TransformationInfoListener listener) {
        TransformationInfo transformationInfo;
        synchronized (mLock) {
            mTransformationInfoListener = listener;
            mTransformationInfoExecutor = executor;
            transformationInfo = mTransformationInfo;
        }
        if (transformationInfo != null) {
            executor.execute(() -> listener.onTransformationInfoUpdate(transformationInfo));
        }
    }

    /**
     * Clears the {@link TransformationInfoListener} set via {@link #setTransformationInfoListener}.
     */
    public void clearTransformationInfoListener() {
        synchronized (mLock) {
            mTransformationInfoListener = null;
            mTransformationInfoExecutor = null;
        }
    }

    /**
     * An exception used to signal that the camera has cancelled a request for a {@link Surface}.
     *
     * <p>This may be set on the {@link ListenableFuture} returned by
     * {@link SurfaceRequest#provideSurface(Surface, Executor, Consumer) when the camera is
     * shutting down or when the surface resolution requirements have changed in order to
     * accommodate newly bound use cases, in which case the {@linkplain Runnable runnables}
     * provided to {@link #addRequestCancellationListener(Executor, Runnable)} will also be
     * invoked on their respective {@link Executor Executors}.
     */
    private static final class RequestCancelledException extends RuntimeException {
        RequestCancelledException(@NonNull String message, @NonNull Throwable cause) {
            super(message, cause);
        }
    }

    /**
     * Listener that receives updates of the {@link TransformationInfo} associated with the
     * {@link SurfaceRequest}.
     */
    public interface TransformationInfoListener {

        /**
         * Called when the {@link TransformationInfo} is updated.
         *
         * <p> This is called when the transformation info becomes available or is updated.
         * The rotation degrees is updated after calling {@link Preview#setTargetRotation}, and the
         * crop rect is updated after changing the {@link ViewPort} associated with the
         * {@link Preview}.
         *
         * @param transformationInfo apply the transformation info to transform {@link Preview}
         * @see TransformationInfo
         * @see Preview#setTargetRotation(int)
         * @see Preview.Builder#setTargetRotation(int)
         * @see CameraCharacteristics#SENSOR_ORIENTATION
         * @see ViewPort
         */
        void onTransformationInfoUpdate(@NonNull TransformationInfo transformationInfo);
    }

    /**
     * Result of providing a surface to a {@link SurfaceRequest} via
     * {@link #provideSurface(Surface, Executor, Consumer)}.
     */
    @AutoValue
    public abstract static class Result {

        /**
         * Possible result codes.
         *
         * @hide
         */
        @IntDef({RESULT_SURFACE_USED_SUCCESSFULLY, RESULT_REQUEST_CANCELLED, RESULT_INVALID_SURFACE,
                RESULT_SURFACE_ALREADY_PROVIDED, RESULT_WILL_NOT_PROVIDE_SURFACE})
        @Retention(RetentionPolicy.SOURCE)
        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
        public @interface ResultCode {
        }

        /**
         * Provided surface was successfully used by the camera and eventually detached once no
         * longer needed by the camera.
         *
         * <p>This result denotes that it is safe to release the {@link Surface} and any underlying
         * resources.
         *
         * <p>For compatibility reasons, the {@link Surface} object should not be reused by
         * future {@link SurfaceRequest SurfaceRequests}, and a new surface should be
         * created instead.
         */
        public static final int RESULT_SURFACE_USED_SUCCESSFULLY = 0;

        /**
         * Provided surface was never attached to the camera due to the {@link SurfaceRequest} being
         * cancelled by the camera.
         *
         * <p>It is safe to release or reuse {@link Surface}, assuming it was not previously
         * attached to a camera via {@link #provideSurface(Surface, Executor, Consumer)}. If
         * reusing the surface for a future surface request, it should be verified that the
         * surface still matches the resolution specified by {@link SurfaceRequest#getResolution()}.
         */
        public static final int RESULT_REQUEST_CANCELLED = 1;

        /**
         * Provided surface could not be used by the camera.
         *
         * <p>This is likely due to the {@link Surface} being closed prematurely or the resolution
         * of the surface not matching the resolution specified by
         * {@link SurfaceRequest#getResolution()}.
         */
        public static final int RESULT_INVALID_SURFACE = 2;

        /**
         * Surface was not attached to the camera through this invocation of
         * {@link #provideSurface(Surface, Executor, Consumer)} due to the {@link SurfaceRequest}
         * already being complete with a surface.
         *
         * <p>The {@link SurfaceRequest} has already been completed by a previous invocation
         * of {@link #provideSurface(Surface, Executor, Consumer)}.
         *
         * <p>It is safe to release or reuse the {@link Surface}, assuming it was not previously
         * attached to a camera via {@link #provideSurface(Surface, Executor, Consumer)}.
         */
        public static final int RESULT_SURFACE_ALREADY_PROVIDED = 3;

        /**
         * Surface was not attached to the camera through this invocation of
         * {@link #provideSurface(Surface, Executor, Consumer)} due to the {@link SurfaceRequest}
         * already being marked as "will not provide surface".
         *
         * <p>The {@link SurfaceRequest} has already been marked as 'will not provide surface' by a
         * previous invocation of {@link #willNotProvideSurface()}.
         *
         * <p>It is safe to release or reuse the {@link Surface}, assuming it was not previously
         * attached to a camera via {@link #provideSurface(Surface, Executor, Consumer)}.
         */
        public static final int RESULT_WILL_NOT_PROVIDE_SURFACE = 4;

        /**
         * Creates a result from the given result code and surface.
         *
         * <p>Can be used to compare to results returned to {@code resultListener} in
         * {@link #provideSurface(Surface, Executor, Consumer)}.
         *
         * @param code    One of {@link #RESULT_SURFACE_USED_SUCCESSFULLY},
         *                {@link #RESULT_REQUEST_CANCELLED}, {@link #RESULT_INVALID_SURFACE},
         *                {@link #RESULT_SURFACE_ALREADY_PROVIDED}, or
         *                {@link #RESULT_WILL_NOT_PROVIDE_SURFACE}.
         * @param surface The {@link Surface} used to complete the {@link SurfaceRequest}.
         */
        @NonNull
        static Result of(@ResultCode int code, @NonNull Surface surface) {
            return new AutoValue_SurfaceRequest_Result(code, surface);
        }

        /**
         * Returns the result of invoking {@link #provideSurface(Surface, Executor, Consumer)}
         * with the surface from {@link #getSurface()}.
         *
         * @return One of {@link #RESULT_SURFACE_USED_SUCCESSFULLY},
         * {@link #RESULT_REQUEST_CANCELLED}, {@link #RESULT_INVALID_SURFACE}, or
         * {@link #RESULT_SURFACE_ALREADY_PROVIDED}, {@link #RESULT_WILL_NOT_PROVIDE_SURFACE}.
         */
        @ResultCode
        public abstract int getResultCode();

        /**
         * The surface used to complete a {@link SurfaceRequest} with
         * {@link #provideSurface(Surface, Executor, Consumer)}.
         *
         * @return the surface.
         */
        @NonNull
        public abstract Surface getSurface();

        // Ensure Result can't be subclassed outside the package
        Result() {
        }
    }

    /**
     * Transformation associated the preview output.
     *
     * <p> The {@link TransformationInfo} can be used transform the {@link Surface} provided via
     * {@link SurfaceRequest#provideSurface}. The info is based on the camera sensor rotation,
     * preview target rotation and the {@link ViewPort} associated with the {@link Preview}. The
     * application of the info depends on the source of the {@link Surface}. For detailed example,
     * please check out the source code of PreviewView in androidx.camera.view artifact.
     *
     * <p> The info is also needed to transform coordinates across use cases. In a face detection
     * example, one common scenario is running a face detection algorithm against a
     * {@link ImageAnalysis} use case, and highlight the detected face in the preview. Below is
     * a code sample to get the transformation {@link Matrix} based on the {@link ImageProxy} from
     * {@link ImageAnalysis} and the {@link TransformationInfo} from {@link Preview}:
     *
     * <pre><code>
     *     // Get rotation transformation.
     *     val transformation = Matrix()
     *     transformation.setRotate(info.getRotationDegrees())
     *
     *     // Get rotated crop rect and cropping transformation.
     *     val rotatedRect = new RectF()
     *     rotation.mapRect(rotatedRect, RectF(imageProxy.getCropRect()))
     *     rotatedRect.sort()
     *     val cropTransformation = Matrix()
     *     cropTransformation.setRectToRect(
     *          RectF(imageProxy.getCropRect()), rotatedRect, ScaleToFit.FILL)
     *
     *     // Concatenate the rotation and cropping transformations.
     *     transformation.postConcat(cropTransformation)
     * </code></pre>
     *
     * @see Preview#setTargetRotation(int)
     * @see Preview.Builder#setTargetRotation(int)
     * @see CameraCharacteristics#SENSOR_ORIENTATION
     * @see ViewPort
     */
    @AutoValue
    public abstract static class TransformationInfo {

        /**
         * Returns the crop rect rectangle.
         *
         * <p> The returned value dictates how the {@link Surface} provided in
         * {@link SurfaceRequest#provideSurface} should be displayed. The crop
         * rectangle specifies the region of valid pixels in the buffer, using coordinates from (0,
         * 0) to the (width, height) of {@link SurfaceRequest#getResolution}. The caller should
         * arrange the UI so that only the valid region is visible to app users.
         *
         * <p> If {@link Preview} is configured with a {@link ViewPort}, this value is calculated
         * based on the configuration of {@link ViewPort}; if not, it returns the full rect of the
         * buffer. For code sample on how to apply the crop rect, please see {@link ViewPort#FIT}.
         *
         * @see ViewPort
         */
        @NonNull
        public abstract Rect getCropRect();

        /**
         * Returns the rotation needed to transform the output from sensor to the target
         * rotation.
         *
         * <p> This is a clockwise rotation in degrees that needs to be applied to the sensor
         * buffer. The rotation will be determined by {@link CameraCharacteristics},
         * {@link Preview#setTargetRotation(int)} and
         * {@link Preview.Builder#setTargetRotation(int)}. This value is useful for transforming
         * coordinates across use cases.
         *
         * <p> This value is most useful for transforming coordinates across use cases, e.g. in
         * preview, highlighting a pattern detected in image analysis. For correcting
         * the preview itself, usually the source of the {@link Surface} handles the rotation
         * without needing this value. For {@link SurfaceView}, it automatically corrects the
         * preview to match the display rotation. For {@link TextureView}, the only additional
         * rotation needed is the display rotation. For detailed example, please check out the
         * source code of PreviewView in androidx.camera .view artifact.
         *
         * @return The rotation in degrees which will be a value in {0, 90, 180, 270}.
         * @see Preview#setTargetRotation(int)
         * @see Preview#getTargetRotation()
         * @see ViewPort
         */
        @ImageOutputConfig.RotationDegreesValue
        public abstract int getRotationDegrees();

        /**
         * The {@linkplain Preview#getTargetRotation() target rotation} of the {@link Preview}.
         *
         * <p>Used to correct preview for {@link TextureView}.
         * {@link #getRotationDegrees()} is a function of 1)
         * {@link CameraCharacteristics#SENSOR_ORIENTATION}, 2) camera lens facing direction and 3)
         * target rotation. {@link TextureView} handles 1) & 2) automatically,
         * while still needs the target rotation to correct the display.This is used when apps
         * need to rotate the preview to non-display orientation.
         *
         * <p>The API is internal for PreviewView to use. For external users, the value
         * is usually {@link Display#getRotation()} in practice. If that's not the case, they can
         * always obtain the value from {@link Preview#getTargetRotation()}.
         *
         * <p>Please note that if the value is {@link ImageOutputConfig#ROTATION_NOT_SPECIFIED}
         * which means targetRotation is not specified for Preview, the user should always get
         * up-to-date display rotation and re-calculate the rotationDegrees to correct the display.
         *
         * @hide
         * @see CameraCharacteristics#SENSOR_ORIENTATION
         */
        @ImageOutputConfig.OptionalRotationValue
        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
        public abstract int getTargetRotation();

        /**
         * Creates new {@link TransformationInfo}
         *
         * <p> Internally public to be used in view artifact tests.
         *
         * @hide
         */
        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
        @NonNull
        public static TransformationInfo of(@NonNull Rect cropRect,
                @ImageOutputConfig.RotationDegreesValue int rotationDegrees,
                @ImageOutputConfig.OptionalRotationValue int targetRotation) {
            return new AutoValue_SurfaceRequest_TransformationInfo(cropRect, rotationDegrees,
                    targetRotation);
        }

        // Hides public constructor.
        TransformationInfo() {
        }
    }
}