public class

RequestMonitor

extends java.lang.Object

 java.lang.Object

↳androidx.camera.camera2.internal.compat.workaround.RequestMonitor

Gradle dependencies

compile group: 'androidx.camera', name: 'camera-camera2', version: '1.5.0-alpha01'

  • groupId: androidx.camera
  • artifactId: camera-camera2
  • version: 1.5.0-alpha01

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

Overview

Monitors in-flight capture sequences on devices with specific quirks.

Quirks on Certain Devices:

Some devices may fail to configure new CameraCaptureSessions if existing in-flight capture sequences haven't completed. This class helps you work around these issues.

Single capture requests may not receive a response if they are submitted simultaneously with repeating capture requests. Single capture requests fail to receive a response approximately 10% of the time when submitted within milliseconds of a repeating capture request.

How it works: Use `RequestMonitor#getRequestsProcessedFuture()` to get a ListenableFuture. This future signals when all in-flight capture sequences have been processed.

Summary

Constructors
publicRequestMonitor(boolean quirkEnabled)

Constructor of the RequestMonitor

Methods
public CameraCaptureSession.CaptureCallbackcreateMonitorListener(CameraCaptureSession.CaptureCallback originalListener)

Creates a listener that monitors request completion for the `RequestMonitor`.

public <any>getRequestsProcessedFuture()

Returns a ListenableFuture that indicates whether all capture requests have been processed.

public booleanshouldMonitorRequest()

Indicates whether capture sequence monitoring is enabled.

public voidstop()

This should be called when a SynchronizedCaptureSession is stopped or closed.

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

Constructors

public RequestMonitor(boolean quirkEnabled)

Constructor of the RequestMonitor

Methods

public boolean shouldMonitorRequest()

Indicates whether capture sequence monitoring is enabled.

Returns true if a quirk is enabled that necessitates tracking in-flight capture requests. Returns false otherwise.

public <any> getRequestsProcessedFuture()

Returns a ListenableFuture that indicates whether all capture requests have been processed.

public CameraCaptureSession.CaptureCallback createMonitorListener(CameraCaptureSession.CaptureCallback originalListener)

Creates a listener that monitors request completion for the `RequestMonitor`.

This listener should be assigned to the CameraCaptureSession via the `setSingleRepeatingRequest` or `captureBurstRequests` method to track when submitted requests are fully processed. The `RequestMonitor` can then use this information to ensure proper capture sequence handling.

Note: the created listener wraps the provided `originalListener`, ensuring any original capture callbacks still function as intended.

Parameters:

originalListener: The original CaptureCallback to combine with monitoring functionality.

Returns:

A new CaptureCallback that includes request completion tracking for the `RequestMonitor`.

public void stop()

This should be called when a SynchronizedCaptureSession is stopped or closed.

Source

/*
 * Copyright 2023 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.camera2.internal.compat.workaround;

import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.camera.camera2.internal.Camera2CaptureCallbacks;
import androidx.camera.camera2.internal.compat.quirk.CaptureNoResponseQuirk;
import androidx.camera.camera2.internal.compat.quirk.CaptureSessionStuckQuirk;
import androidx.camera.camera2.internal.compat.quirk.IncorrectCaptureStateQuirk;
import androidx.camera.core.impl.annotation.ExecutedBy;
import androidx.camera.core.impl.utils.executor.CameraXExecutors;
import androidx.camera.core.impl.utils.futures.Futures;
import androidx.concurrent.futures.CallbackToFutureAdapter;

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

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;

/**
 * Monitors in-flight capture sequences on devices with specific quirks.
 *
 * <p>Quirks on Certain Devices:
 * <p>Some devices may fail to configure new CameraCaptureSessions
 * if existing in-flight capture sequences haven't completed. This class helps you work around
 * these issues.
 * <p>Single capture requests may not receive a response if they are submitted
 * simultaneously with repeating capture requests. Single capture requests fail to receive a
 * response approximately 10% of the time when submitted within milliseconds of a repeating
 * capture request.
 *
 * <p>How it works: Use `RequestMonitor#getRequestsProcessedFuture()` to get a ListenableFuture.
 * This future signals when all in-flight capture sequences have been processed.
 *
 * @see CaptureNoResponseQuirk
 * @see CaptureSessionStuckQuirk
 * @see IncorrectCaptureStateQuirk
 */
public class RequestMonitor {

    private static final String TAG = "RequestMonitor";
    private final boolean mQuirkEnabled;
    private final List<ListenableFuture<Void>> mRequestTasks =
            Collections.synchronizedList(new ArrayList<>());

    /** Constructor of the RequestMonitor */
    public RequestMonitor(boolean quirkEnabled) {
        mQuirkEnabled = quirkEnabled;
    }

    /**
     * Indicates whether capture sequence monitoring is enabled.
     *
     * <p>Returns true if a quirk is enabled that necessitates tracking in-flight capture requests.
     * Returns false otherwise.
     */
    public boolean shouldMonitorRequest() {
        return mQuirkEnabled;
    }

    /**
     * Returns a ListenableFuture that indicates whether all capture requests have been
     * processed.
     */
    @ExecutedBy("mExecutor")
    @NonNull
    public ListenableFuture<Void> getRequestsProcessedFuture() {
        if (mRequestTasks.isEmpty()) {
            return Futures.immediateFuture(null);
        }

        return Futures.nonCancellationPropagating(
                Futures.transform(Futures.successfulAsList(new ArrayList<>(mRequestTasks)),
                        input -> null, CameraXExecutors.directExecutor()));
    }

    /**
     * Creates a listener that monitors request completion for the `RequestMonitor`.
     *
     * <p>This listener should be assigned to the CameraCaptureSession via
     * the `setSingleRepeatingRequest` or `captureBurstRequests` method to track when submitted
     * requests are fully processed.
     * The `RequestMonitor` can then use this information to ensure proper capture sequence
     * handling.
     *
     * <p>Note: the created listener wraps the provided `originalListener`, ensuring any original
     * capture callbacks still function as intended.
     *
     * @param originalListener The original CaptureCallback to combine with monitoring
     *                         functionality.
     * @return A new CaptureCallback that includes request completion tracking for the
     * `RequestMonitor`.
     */
    @ExecutedBy("mExecutor")
    @NonNull
    public CameraCaptureSession.CaptureCallback createMonitorListener(
            @NonNull CameraCaptureSession.CaptureCallback originalListener) {
        if (shouldMonitorRequest()) {
            return Camera2CaptureCallbacks.createComboCallback(createMonitorListener(),
                    originalListener);
        } else {
            return originalListener;
        }
    }

    private CameraCaptureSession.CaptureCallback createMonitorListener() {
        RequestCompleteListener completeListener = new RequestCompleteListener();
        ListenableFuture<Void> future = completeListener.mStartRequestFuture;

        mRequestTasks.add(future);
        Log.d(TAG, "RequestListener " + completeListener + " monitoring " + this);
        future.addListener(() -> {
            Log.d(TAG, "RequestListener " + completeListener + " done " + this);
            mRequestTasks.remove(future);
        }, CameraXExecutors.directExecutor());
        return completeListener;
    }

    /** This should be called when a SynchronizedCaptureSession is stopped or closed. */
    @ExecutedBy("mExecutor")
    public void stop() {
        LinkedList<ListenableFuture<Void>> tasks = new LinkedList<>(mRequestTasks);
        while (!tasks.isEmpty()) {
            Objects.requireNonNull(tasks.poll()).cancel(true);
        }
    }

    static class RequestCompleteListener extends CameraCaptureSession.CaptureCallback {
        @NonNull
        final ListenableFuture<Void> mStartRequestFuture;
        @SuppressWarnings("WeakerAccess") /* synthetic accessor */
        CallbackToFutureAdapter.Completer<Void> mStartRequestCompleter;

        RequestCompleteListener() {
            mStartRequestFuture = CallbackToFutureAdapter.getFuture(completer -> {
                mStartRequestCompleter = completer;
                return "RequestCompleteListener[" + this + "]";
            });
        }

        @Override
        public void onCaptureStarted(@NonNull CameraCaptureSession session,
                @NonNull CaptureRequest request, long timestamp, long frameNumber) {
            completeFuture();
        }

        @Override
        public void onCaptureSequenceAborted(@NonNull CameraCaptureSession session,
                int sequenceId) {
            completeFuture();
        }

        @Override
        public void onCaptureCompleted(@NonNull CameraCaptureSession session,
                @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
            completeFuture();
        }

        @Override
        public void onCaptureFailed(@NonNull CameraCaptureSession session,
                @NonNull CaptureRequest request, @NonNull CaptureFailure failure) {
            completeFuture();
        }

        @Override
        public void onCaptureSequenceCompleted(@NonNull CameraCaptureSession session,
                int sequenceId, long frameNumber) {
            completeFuture();
        }

        private void completeFuture() {
            if (mStartRequestCompleter != null) {
                mStartRequestCompleter.set(null);
                mStartRequestCompleter = null;
            }
        }
    }
}