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

Commit 301abd49 authored by Emilian Peev's avatar Emilian Peev
Browse files

Camera: Add initial offline camera session implementation

Offline camera sessions allow clients to migrate still
ongoing capture requests to offline processing mode.
When offline mode is enabled, the initial active capture
session along with the open camera device can be closed
and further re-used while offline processing is still
ongoing.
Split and re-use parts of camera device implementation
along with its current state for offline mode.

Bug: 135142453
Test: Camera CTS
Change-Id: I5eb4055e2af4265ece0fb7271bdf5f4cf2d5aaf3
parent d56a615a
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -16953,6 +16953,7 @@ package android.hardware.camera2 {
    method public abstract int setRepeatingRequest(@NonNull android.hardware.camera2.CaptureRequest, @Nullable android.hardware.camera2.CameraCaptureSession.CaptureCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
    method public int setSingleRepeatingRequest(@NonNull android.hardware.camera2.CaptureRequest, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraCaptureSession.CaptureCallback) throws android.hardware.camera2.CameraAccessException;
    method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException;
    method @Nullable public android.hardware.camera2.CameraOfflineSession switchToOffline(@NonNull java.util.Collection<android.view.Surface>, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraOfflineSession.CameraOfflineSessionCallback) throws android.hardware.camera2.CameraAccessException;
    method public void updateOutputConfiguration(android.hardware.camera2.params.OutputConfiguration) throws android.hardware.camera2.CameraAccessException;
  }
@@ -17377,6 +17378,20 @@ package android.hardware.camera2 {
    field public static final int TONEMAP_PRESET_CURVE_SRGB = 0; // 0x0
  }
  public abstract class CameraOfflineSession extends android.hardware.camera2.CameraCaptureSession {
    ctor public CameraOfflineSession();
  }
  public abstract static class CameraOfflineSession.CameraOfflineSessionCallback {
    ctor public CameraOfflineSession.CameraOfflineSessionCallback();
    method public abstract void onClosed(@NonNull android.hardware.camera2.CameraOfflineSession);
    method public abstract void onError(@NonNull android.hardware.camera2.CameraOfflineSession, int);
    method public abstract void onIdle(@NonNull android.hardware.camera2.CameraOfflineSession);
    method public abstract void onReady(@NonNull android.hardware.camera2.CameraOfflineSession);
    method public abstract void onSwitchFailed(@NonNull android.hardware.camera2.CameraOfflineSession);
    field public static final int STATUS_INTERNAL_ERROR = 0; // 0x0
  }
  public class CaptureFailure {
    method public long getFrameNumber();
    method @Nullable public String getPhysicalCameraId();
+66 −0
Original line number Diff line number Diff line
@@ -19,10 +19,13 @@ package android.hardware.camera2;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.camera2.CameraOfflineSession;
import android.hardware.camera2.CameraOfflineSession.CameraOfflineSessionCallback;
import android.hardware.camera2.params.OutputConfiguration;
import android.os.Handler;
import android.view.Surface;

import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executor;

@@ -854,6 +857,69 @@ public abstract class CameraCaptureSession implements AutoCloseable {
        throw new UnsupportedOperationException("Subclasses must override this method");
    }

    /**
     * Switch the current capture session and a given set of registered camera surfaces
     * to offline processing mode.
     *
     * <p>Offline processing mode and the corresponding {@link CameraOfflineSession} differ from
     * a regular online camera capture session in several ways. Successful offline switches will
     * close the currently active camera capture session. Camera clients are also allowed
     * to call {@link CameraDevice#close} while offline processing of selected capture
     * requests is still in progress. Such side effects free device close is only possible
     * when the offline session moves to the ready state. Once this happens, closing the camera
     * device will not affect the pending offline requests and they must complete as normal.</p>
     *
     * <p>Offline processing mode switches may need several hundred milliseconds to complete
     * as the underlying camera implementation must abort all currently active repeating requests
     * as well as all other pending requests not specified by the client. Additionally the switch
     * will be blocked until all requests that continue processing within the offline session
     * acquire their initial input frame from camera sensor. The call to {@link #switchToOffline}
     * itself is not blocking and will only trigger the offline switch sequence. Clients will
     * be notified via {@link CameraOfflineSessionCallback#onReady} once the entire sequence is
     * complete.</p>
     *
     * <p>Please note that after a successful call to this method the currently active capture
     * session will no longer be valid and clients will begin receiving capture
     * callbacks with a corresponding {@link CameraOfflineSession} passed as a session
     * argument.</p>
     *
     * @param offlineSurfaces Client-specified collection of input/output camera registered surfaces
     *                        that need to be switched to offline mode along with their pending
     *                        capture requests. Do note that not all camera registered
     *                        surfaces can be switched to offline mode.
     *                        Shared surfaces {@link OutputConfiguration#enableSurfaceSharing}
     *                        and surfaces as part of a surface group do not support offline
     *                        switches.
     *
     * @param executor the executor which will be used for invoking the offline callback listener.
     *
     * @param listener The callback object to notify for offline session events.
     *
     * @return camera offline session which in case of successful offline switch will move in ready
     *         state after clients receive {@link CameraOfflineSessionCallback#onReady}. In case the
     *         offline switch was not successful clients will receive respective
     *         {@link CameraOfflineSessionCallback#onSwitchFailed} notification.
     *
     * @throws IllegalArgumentException if an attempt was made to pass a {@link Surface} that was
     *                                  not registered with this capture session or a shared
     *                                  surface {@link OutputConfiguration#enableSurfaceSharing} or
     *                                  surface as part of a surface group. The current capture
     *                                  session will remain valid and active in case of this
     *                                  exception.
     *
     * @throws CameraAccessException if the camera device is no longer connected or has
     *                               encountered a fatal error.
     *
     * @see CameraOfflineSession
     * @see CameraOfflineSessionCallback
     */
    @Nullable
    public CameraOfflineSession switchToOffline(@NonNull Collection<Surface> offlineSurfaces,
            @NonNull Executor executor, @NonNull CameraOfflineSessionCallback listener)
            throws CameraAccessException {
        throw new UnsupportedOperationException("Subclasses must override this method");
    }

    /**
     * Close this capture session asynchronously.
     *
+159 −0
Original line number Diff line number Diff line
/*
 * Copyright 2019 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 android.hardware.camera2;

import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;

import android.annotation.IntDef;
import android.annotation.NonNull;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * A camera capture session that was switched to offline mode via successful call to
 * {@link CameraCaptureSession#switchToOffline}.
 *
 * <p>Offline capture sessions allow clients to select a set of camera registered surfaces that
 * support offline mode.  After a successful offline mode switch all non-repeating pending requests
 * on those surfaces will continue to be processed by the camera stack even if clients close the
 * corresponding camera device.<p>
 *
 * <p>Offline capture session instances will replace the previously active capture session arguments
 * in all capture callbacks after {@link CameraCaptureSession#switchToOffline} completes.</p>
 *
 * <p>Processing of pending offline capture requests will begin only after the offline session
 * moves to ready state which will be indicated by the {@link CameraOfflineSessionCallback#onReady}
 * callback.</p>
 *
 * <p>In contrast to a regular {@link CameraCaptureSession} an offline capture session will
 * not accept any further capture requests. Besides {@link CameraOfflineSession#close} all
 * remaining methods will throw {@link UnsupportedOperationException} and are not supported.</p>
 *
 * @see CameraCaptureSession#supportsOfflineProcessing
 */
public abstract class CameraOfflineSession extends CameraCaptureSession {
    public static abstract class CameraOfflineSessionCallback {
        /**
         * This method indicates that the offline switch call
         * {@link CameraCaptureSession#switchToOffline} was successful.
         *
         * <p>This callback will be invoked once the offline session moves to the ready state.</p>
         *
         * <p>Calls to {@link CameraDevice#close} will not have impact on the processing of offline
         * requests once the offline session moves in ready state.</p>
         *
         * @param session the currently ready offline session
         *
         */
        public abstract void onReady(@NonNull CameraOfflineSession session);

        /**
         * This method indicates that the offline switch call
         * {@link CameraCaptureSession#switchToOffline} was not able to complete successfully.
         *
         * <p>The offline switch can fail either due to internal camera error during the switch
         * sequence or because the camera implementation was not able to find any pending capture
         * requests that can be migrated to offline mode.</p>
         *
         * <p>Calling {@link CameraOfflineSession#close} is not necessary and clients will not
         * receive any further offline session notifications.</p>
         *
         * @param session the offline session that failed to switch to ready state
         */
        public abstract void onSwitchFailed(@NonNull CameraOfflineSession session);

        /**
         * This method indicates that all pending offline requests were processed.
         *
         * <p>This callback will be invoked once the offline session finishes processing
         * all of its pending offline capture requests.</p>
         *
         * @param session the currently ready offline session
         *
         */
        public abstract void onIdle(@NonNull CameraOfflineSession session);

        /**
         * This status code indicates unexpected and fatal internal camera error.
         *
         * <p>Pending offline requests will be discarded and the respective registered
         * capture callbacks may not get triggered.</p>
         *
         * @see #onError
         */
        public static final int STATUS_INTERNAL_ERROR = 0;

        /** @hide */
        @Retention(RetentionPolicy.SOURCE)
        @IntDef(prefix = {"STATUS_"}, value = {STATUS_INTERNAL_ERROR})
        public @interface StatusCode {};

        /**
         * This method is called when the offline session encounters an unexpected error.
         *
         * <p>This notification will only be invoked for sessions that reached the ready state.
         * Clients will need to call {@link CameraOfflineSession#close} to close and release all
         * resources. {@link #onClosed} will not be triggered automatically in error scenarios.</p>
         *
         * @param session the current offline session
         * @param status error status
         *
         */
        public abstract void onError(@NonNull CameraOfflineSession session, @StatusCode int status);

        /**
         * This method is called when the offline session is closed.
         *
         * <p>An offline session will be closed after a call to
         * {@link CameraOfflineSession#close}.</p>
         *
         * <p>In case of failure to switch to offline mode, only {@link #onSwitchFailed} will be
         * called and {@link #onClosed} will not be.</p>
         *
         * <p>In case there was no previous {@link #onIdle} notification any in-progress
         * offline capture requests within the offline session will be discarded
         * and further result callbacks will not be triggered.</p>
         *
         * @param session the session returned by {@link CameraCaptureSession#switchToOffline}
         *
         */
        public abstract void onClosed(@NonNull CameraOfflineSession session);
    }

    /**
     * Close this offline capture session.
     *
     * <p>Abort all pending offline requests and close the connection to the offline camera session
     * as quickly as possible.</p>
     *
     * <p>This method can be called only after clients receive
     * {@link CameraOfflineSessionCallback#onReady}.</p>
     *
     * <p>Immediately after this call, besides the final
     * {@link CameraOfflineSessionCallback#onClosed} notification, no further callbacks from the
     * offline session will be triggered and all remaining offline capture requests will be
     * discarded.</p>
     *
     * <p>Closing a session is idempotent; closing more than once has no effect.</p>
     *
     * @throws IllegalStateException if the offline sesion is not ready.
     */
    @Override
    public abstract void close();
}
+10 −0
Original line number Diff line number Diff line
@@ -61,4 +61,14 @@ public interface CameraCaptureSessionCore {
     */
    boolean isAborting();

    /**
     * Close the capture session without draining the pending requests.
     *
     * <p>This is usually used when switching to offline session mode. Depending
     * on the client input, some of the pending requests will be flushed and some
     * will remain for further processing. In either case, the regular drain logic
     * needs to be skipped.</p>
     *
     */
    void closeWithoutDraining();
}
+53 −10
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package android.hardware.camera2.impl;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraOfflineSession;
import android.hardware.camera2.CameraOfflineSession.CameraOfflineSessionCallback;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.ICameraDeviceUser;
import android.hardware.camera2.params.OutputConfiguration;
@@ -29,6 +31,7 @@ import android.util.Log;
import android.view.Surface;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executor;

@@ -106,7 +109,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession
         * Use the same handler as the device's StateCallback for all the internal coming events
         *
         * This ensures total ordering between CameraDevice.StateCallback and
         * CameraDeviceImpl.CaptureCallback events.
         * CaptureCallback events.
         */
        mSequenceDrainer = new TaskDrainer<>(mDeviceExecutor, new SequenceDrainListener(),
                /*name*/"seq");
@@ -136,24 +139,36 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession

    @Override
    public void prepare(Surface surface) throws CameraAccessException {
        synchronized (mDeviceImpl.mInterfaceLock) {
            checkNotClosed();
            mDeviceImpl.prepare(surface);
        }
    }

    @Override
    public void prepare(int maxCount, Surface surface) throws CameraAccessException {
        synchronized (mDeviceImpl.mInterfaceLock) {
            checkNotClosed();
            mDeviceImpl.prepare(maxCount, surface);
        }
    }

    @Override
    public void tearDown(Surface surface) throws CameraAccessException {
        synchronized (mDeviceImpl.mInterfaceLock) {
            checkNotClosed();
            mDeviceImpl.tearDown(surface);
        }
    }

    @Override
    public void finalizeOutputConfigurations(
            List<OutputConfiguration> outputConfigs) throws CameraAccessException {
        synchronized (mDeviceImpl.mInterfaceLock) {
            checkNotClosed();
            mDeviceImpl.finalizeOutputConfigs(outputConfigs);
        }
    }

    @Override
    public int capture(CaptureRequest request, CaptureCallback callback,
@@ -445,6 +460,15 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession
        }
    }

    @Override
    public CameraOfflineSession switchToOffline(Collection<Surface> offlineOutputs,
            Executor executor, CameraOfflineSessionCallback listener) throws CameraAccessException {
        synchronized (mDeviceImpl.mInterfaceLock) {
            checkNotClosed();
        }
        return mDeviceImpl.switchToOffline(offlineOutputs, executor, listener);
    }

    @Override
    public boolean isReprocessable() {
        return mInput != null;
@@ -500,6 +524,25 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession
        }
    }

    @Override
    public void closeWithoutDraining() {
        synchronized (mDeviceImpl.mInterfaceLock) {
            if (mClosed) {
                if (DEBUG) Log.v(TAG, mIdString + "close - reentering");
                return;
            }

            if (DEBUG) Log.v(TAG, mIdString + "close - first time");

            mClosed = true;
            mStateCallback.onClosed(this);
        }

        if (mInput != null) {
            mInput.release();
        }
    }

    @Override
    public void close() {
        synchronized (mDeviceImpl.mInterfaceLock) {
@@ -571,13 +614,13 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession
    }

    /**
     * Forward callbacks from
     * CameraDeviceImpl.CaptureCallback to the CameraCaptureSession.CaptureCallback.
     * Forward callbacks that usually originate from
     * CameraDeviceImpl.CameraDeviceCallbacks to the CameraCaptureSession.CaptureCallback.
     *
     * <p>When a capture sequence finishes, update the pending checked sequences set.</p>
     */
    @SuppressWarnings("deprecation")
    private CameraDeviceImpl.CaptureCallback createCaptureCallbackProxy(
    private android.hardware.camera2.impl.CaptureCallback createCaptureCallbackProxy(
            Handler handler, CaptureCallback callback) {
        final Executor executor = (callback != null) ? CameraDeviceImpl.checkAndWrapHandler(
                handler) : null;
@@ -585,9 +628,9 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession
        return createCaptureCallbackProxyWithExecutor(executor, callback);
    }

    private CameraDeviceImpl.CaptureCallback createCaptureCallbackProxyWithExecutor(
    private android.hardware.camera2.impl.CaptureCallback createCaptureCallbackProxyWithExecutor(
            Executor executor, CaptureCallback callback) {
        return new CameraDeviceImpl.CaptureCallback() {
        return new android.hardware.camera2.impl.CaptureCallback(executor, callback) {
            @Override
            public void onCaptureStarted(CameraDevice camera,
                    CaptureRequest request, long timestamp, long frameNumber) {
Loading