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

Commit e663cb77 authored by Ruben Brunk's avatar Ruben Brunk
Browse files

camera2: refactor LEGACY mode error handling.

Bug: 17431462
Bug: 17407537

- Add Surface format/size validation during configure.
- Update exception error codes used in binder calls.
- Report dropped requests, frames, captures, and device
  errors in binder callback properly.
- Fixes CameraDeviceTest errors for incorrect metering
  rectangle weight in template tests.
- Fixes CameraDeviceTest errors for missing
  noiseReductionMode field in template tests.
- Implement flush call.

Change-Id: I0da803bccf2bfb9b4c0cf61208e160a86c577497
parent 2b5f91fe
Loading
Loading
Loading
Loading
+9 −16
Original line number Diff line number Diff line
@@ -51,7 +51,6 @@ import java.util.TreeSet;
 * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
 */
public class CameraDeviceImpl extends CameraDevice {

    private final String TAG;
    private final boolean DEBUG;

@@ -1136,7 +1135,6 @@ public class CameraDeviceImpl extends CameraDevice {
    }

    public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {

        //
        // Constants below need to be kept up-to-date with
        // frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h
@@ -1149,34 +1147,29 @@ public class CameraDeviceImpl extends CameraDevice {
        /**
         * Camera has been disconnected
         */
        static final int ERROR_CAMERA_DISCONNECTED = 0;

        public static final int ERROR_CAMERA_DISCONNECTED = 0;
        /**
         * Camera has encountered a device-level error
         * Matches CameraDevice.StateCallback#ERROR_CAMERA_DEVICE
         */
        static final int ERROR_CAMERA_DEVICE = 1;

        public static final int ERROR_CAMERA_DEVICE = 1;
        /**
         * Camera has encountered a service-level error
         * Matches CameraDevice.StateCallback#ERROR_CAMERA_SERVICE
         */
        static final int ERROR_CAMERA_SERVICE = 2;

        public static final int ERROR_CAMERA_SERVICE = 2;
        /**
         * Camera has encountered an error processing a single request.
         */
        static final int ERROR_CAMERA_REQUEST = 3;

        public static final int ERROR_CAMERA_REQUEST = 3;
        /**
         * Camera has encountered an error producing metadata for a single capture
         */
        static final int ERROR_CAMERA_RESULT = 4;

        public static final int ERROR_CAMERA_RESULT = 4;
        /**
         * Camera has encountered an error producing an image buffer for a single capture
         */
        static final int ERROR_CAMERA_BUFFER = 5;
        public static final int ERROR_CAMERA_BUFFER = 5;

        @Override
        public IBinder asBinder() {
+59 −22
Original line number Diff line number Diff line
@@ -49,6 +49,9 @@ public class CameraDeviceState {
    private static final int STATE_IDLE = 3;
    private static final int STATE_CAPTURING = 4;

    private static final String[] sStateNames = { "ERROR", "UNCONFIGURED", "CONFIGURING", "IDLE",
            "CAPTURING"};

    private int mCurrentState = STATE_UNCONFIGURED;
    private int mCurrentError = CameraBinderDecorator.NO_ERROR;

@@ -57,6 +60,11 @@ public class CameraDeviceState {
    private Handler mCurrentHandler = null;
    private CameraDeviceStateListener mCurrentListener = null;

    /**
     * Error code used by {@link #setCaptureStart} and {@link #setCaptureResult} to indicate that no
     * error has occurred.
     */
    public static final int NO_CAPTURE_ERROR = -1;

    /**
     * CameraDeviceStateListener callbacks to be called after state transitions.
@@ -126,11 +134,15 @@ public class CameraDeviceState {
     *
     * @param request A {@link RequestHolder} containing the request for the current capture.
     * @param timestamp The timestamp of the capture start in nanoseconds.
     * @param captureError Report a recoverable error for a single request using a valid
     *                     error code for {@code ICameraDeviceCallbacks}, or
     *                     {@link #NO_CAPTURE_ERROR}
     * @return {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
     */
    public synchronized int setCaptureStart(final RequestHolder request, long timestamp) {
    public synchronized int setCaptureStart(final RequestHolder request, long timestamp,
                                            int captureError) {
        mCurrentRequest = request;
        doStateTransition(STATE_CAPTURING, timestamp);
        doStateTransition(STATE_CAPTURING, timestamp, captureError);
        return mCurrentError;
    }

@@ -144,12 +156,16 @@ public class CameraDeviceState {
     * the {@code ERROR} state,
     * </p>
     *
     * @param request the {@link RequestHolder} request that created this result.
     * @param result the {@link CameraMetadataNative} result to set.
     * @param request The {@link RequestHolder} request that created this result.
     * @param result The {@link CameraMetadataNative} result to set.
     * @param captureError Report a recoverable error for a single buffer or result using a valid
     *                     error code for {@code ICameraDeviceCallbacks}, or
     *                     {@link #NO_CAPTURE_ERROR}.
     * @return {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
     */
    public synchronized int setCaptureResult(final RequestHolder request,
                                             final CameraMetadataNative result) {
                                             final CameraMetadataNative result,
                                             final int captureError) {
        if (mCurrentState != STATE_CAPTURING) {
            Log.e(TAG, "Cannot receive result while in state: " + mCurrentState);
            mCurrentError = CameraBinderDecorator.INVALID_OPERATION;
@@ -158,6 +174,14 @@ public class CameraDeviceState {
        }

        if (mCurrentHandler != null && mCurrentListener != null) {
            if (captureError != NO_CAPTURE_ERROR) {
                mCurrentHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mCurrentListener.onError(captureError, request);
                    }
                });
            } else {
                mCurrentHandler.post(new Runnable() {
                    @Override
                    public void run() {
@@ -165,6 +189,7 @@ public class CameraDeviceState {
                    }
                });
            }
        }
        return mCurrentError;
    }

@@ -181,14 +206,16 @@ public class CameraDeviceState {
    }

    private void doStateTransition(int newState) {
        doStateTransition(newState, /*timestamp*/0);
        doStateTransition(newState, /*timestamp*/0, CameraBinderDecorator.NO_ERROR);
    }

    private void doStateTransition(int newState, final long timestamp) {
        if (DEBUG) {
    private void doStateTransition(int newState, final long timestamp, final int error) {
        if (newState != mCurrentState) {
                Log.d(TAG, "Transitioning to state " + newState);
            String stateName = "UNKNOWN";
            if (newState >= 0 && newState < sStateNames.length) {
                stateName = sStateNames[newState];
            }
            Log.i(TAG, "Legacy camera service transitioning to state " + stateName);
        }
        switch(newState) {
            case STATE_ERROR:
@@ -251,7 +278,16 @@ public class CameraDeviceState {
                    doStateTransition(STATE_ERROR);
                    break;
                }

                if (mCurrentHandler != null && mCurrentListener != null) {
                    if (error != NO_CAPTURE_ERROR) {
                        mCurrentHandler.post(new Runnable() {
                            @Override
                            public void run() {
                                mCurrentListener.onError(error, mCurrentRequest);
                            }
                        });
                    } else {
                        mCurrentHandler.post(new Runnable() {
                            @Override
                            public void run() {
@@ -259,6 +295,7 @@ public class CameraDeviceState {
                            }
                        });
                    }
                }
                mCurrentState = STATE_CAPTURING;
                break;
            default:
+57 −1
Original line number Diff line number Diff line
@@ -341,6 +341,10 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
            Log.d(TAG, "disconnect called.");
        }

        if (mLegacyDevice.isClosed()) {
            Log.w(TAG, "Cannot disconnect, device has already been closed.");
        }

        try {
            mLegacyDevice.close();
        } finally {
@@ -355,6 +359,11 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
        if (DEBUG) {
            Log.d(TAG, "submitRequest called.");
        }
        if (mLegacyDevice.isClosed()) {
            Log.e(TAG, "Cannot submit request, device has been closed.");
            return CameraBinderDecorator.ENODEV;
        }

        synchronized(mConfigureLock) {
            if (mConfiguring) {
                Log.e(TAG, "Cannot submit request, configuration change in progress.");
@@ -370,6 +379,11 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
        if (DEBUG) {
            Log.d(TAG, "submitRequestList called.");
        }
        if (mLegacyDevice.isClosed()) {
            Log.e(TAG, "Cannot submit request list, device has been closed.");
            return CameraBinderDecorator.ENODEV;
        }

        synchronized(mConfigureLock) {
            if (mConfiguring) {
                Log.e(TAG, "Cannot submit request, configuration change in progress.");
@@ -384,6 +398,11 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
        if (DEBUG) {
            Log.d(TAG, "cancelRequest called.");
        }
        if (mLegacyDevice.isClosed()) {
            Log.e(TAG, "Cannot cancel request, device has been closed.");
            return CameraBinderDecorator.ENODEV;
        }

        synchronized(mConfigureLock) {
            if (mConfiguring) {
                Log.e(TAG, "Cannot cancel request, configuration change in progress.");
@@ -400,6 +419,11 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
        if (DEBUG) {
            Log.d(TAG, "beginConfigure called.");
        }
        if (mLegacyDevice.isClosed()) {
            Log.e(TAG, "Cannot begin configure, device has been closed.");
            return CameraBinderDecorator.ENODEV;
        }

        synchronized(mConfigureLock) {
            if (mConfiguring) {
                Log.e(TAG, "Cannot begin configure, configuration change already in progress.");
@@ -415,6 +439,11 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
        if (DEBUG) {
            Log.d(TAG, "endConfigure called.");
        }
        if (mLegacyDevice.isClosed()) {
            Log.e(TAG, "Cannot end configure, device has been closed.");
            return CameraBinderDecorator.ENODEV;
        }

        ArrayList<Surface> surfaces = null;
        synchronized(mConfigureLock) {
            if (!mConfiguring) {
@@ -438,6 +467,11 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
        if (DEBUG) {
            Log.d(TAG, "deleteStream called.");
        }
        if (mLegacyDevice.isClosed()) {
            Log.e(TAG, "Cannot delete stream, device has been closed.");
            return CameraBinderDecorator.ENODEV;
        }

        synchronized(mConfigureLock) {
            if (!mConfiguring) {
                Log.e(TAG, "Cannot delete stream, beginConfigure hasn't been called yet.");
@@ -458,6 +492,11 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
        if (DEBUG) {
            Log.d(TAG, "createStream called.");
        }
        if (mLegacyDevice.isClosed()) {
            Log.e(TAG, "Cannot create stream, device has been closed.");
            return CameraBinderDecorator.ENODEV;
        }

        synchronized(mConfigureLock) {
            if (!mConfiguring) {
                Log.e(TAG, "Cannot create stream, beginConfigure hasn't been called yet.");
@@ -474,6 +513,10 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
        if (DEBUG) {
            Log.d(TAG, "createDefaultRequest called.");
        }
        if (mLegacyDevice.isClosed()) {
            Log.e(TAG, "Cannot create default request, device has been closed.");
            return CameraBinderDecorator.ENODEV;
        }

        CameraMetadataNative template;
        try {
@@ -503,6 +546,11 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
        if (DEBUG) {
            Log.d(TAG, "waitUntilIdle called.");
        }
        if (mLegacyDevice.isClosed()) {
            Log.e(TAG, "Cannot wait until idle, device has been closed.");
            return CameraBinderDecorator.ENODEV;
        }

        synchronized(mConfigureLock) {
            if (mConfiguring) {
                Log.e(TAG, "Cannot wait until idle, configuration change in progress.");
@@ -518,13 +566,21 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
        if (DEBUG) {
            Log.d(TAG, "flush called.");
        }
        if (mLegacyDevice.isClosed()) {
            Log.e(TAG, "Cannot flush, device has been closed.");
            return CameraBinderDecorator.ENODEV;
        }

        synchronized(mConfigureLock) {
            if (mConfiguring) {
                Log.e(TAG, "Cannot flush, configuration change in progress.");
                return CameraBinderDecorator.INVALID_OPERATION;
            }
        }
        // TODO: implement flush.
        long lastFrame = mLegacyDevice.flush();
        if (lastFrameNumber != null) {
            lastFrameNumber.setNumber(lastFrame);
        }
        return CameraBinderDecorator.NO_ERROR;
    }

+171 −9
Original line number Diff line number Diff line
@@ -15,12 +15,14 @@
 */
package android.hardware.camera2.legacy;

import android.hardware.camera2.impl.CameraDeviceImpl;
import android.util.Log;
import android.util.MutableLong;
import android.util.Pair;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
@@ -44,7 +46,7 @@ public class CaptureCollector {

    private static final int MAX_JPEGS_IN_FLIGHT = 1;

    private class CaptureHolder {
    private class CaptureHolder implements Comparable<CaptureHolder>{
        private final RequestHolder mRequest;
        private final LegacyRequest mLegacy;
        public final boolean needsJpeg;
@@ -53,6 +55,10 @@ public class CaptureCollector {
        private long mTimestamp = 0;
        private int mReceivedFlags = 0;
        private boolean mHasStarted = false;
        private boolean mFailedJpeg = false;
        private boolean mFailedPreview = false;
        private boolean mCompleted = false;
        private boolean mPreviewCompleted = false;

        public CaptureHolder(RequestHolder request, LegacyRequest legacyHolder) {
            mRequest = request;
@@ -74,11 +80,43 @@ public class CaptureCollector {
        }

        public void tryComplete() {
            if (isCompleted()) {
                if (needsPreview && isPreviewCompleted()) {
            if (!mPreviewCompleted && needsPreview && isPreviewCompleted()) {
                CaptureCollector.this.onPreviewCompleted();
                mPreviewCompleted = true;
            }

            if (isCompleted() && !mCompleted) {
                if (mFailedPreview || mFailedJpeg) {
                    if (!mHasStarted) {
                        // Send a request error if the capture has not yet started.
                        mRequest.failRequest();
                        CaptureCollector.this.mDeviceState.setCaptureStart(mRequest, mTimestamp,
                                CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_REQUEST);
                    } else {
                        // Send buffer dropped errors for each pending buffer if the request has
                        // started.
                        if (mFailedPreview) {
                            Log.w(TAG, "Preview buffers dropped for request: " +
                                    mRequest.getRequestId());
                            for (int i = 0; i < mRequest.numPreviewTargets(); i++) {
                                CaptureCollector.this.mDeviceState.setCaptureResult(mRequest,
                                    /*result*/null,
                                        CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_BUFFER);
                            }
                        }
                        if (mFailedJpeg) {
                            Log.w(TAG, "Jpeg buffers dropped for request: " +
                                    mRequest.getRequestId());
                            for (int i = 0; i < mRequest.numJpegTargets(); i++) {
                                CaptureCollector.this.mDeviceState.setCaptureResult(mRequest,
                                    /*result*/null,
                                        CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_BUFFER);
                            }
                        }
                    }
                }
                CaptureCollector.this.onRequestCompleted(this);
                CaptureCollector.this.onRequestCompleted(CaptureHolder.this);
                mCompleted = true;
            }
        }

@@ -103,7 +141,8 @@ public class CaptureCollector {

            if (!mHasStarted) {
                mHasStarted = true;
                CaptureCollector.this.mDeviceState.setCaptureStart(mRequest, mTimestamp);
                CaptureCollector.this.mDeviceState.setCaptureStart(mRequest, mTimestamp,
                        CameraDeviceState.NO_CAPTURE_ERROR);
            }

            tryComplete();
@@ -126,6 +165,20 @@ public class CaptureCollector {
            tryComplete();
        }

        public void setJpegFailed() {
            if (DEBUG) {
                Log.d(TAG, "setJpegFailed - called for request " + mRequest.getRequestId());
            }
            if (!needsJpeg || isJpegCompleted()) {
                return;
            }
            mFailedJpeg = true;

            mReceivedFlags |= FLAG_RECEIVED_JPEG;
            mReceivedFlags |= FLAG_RECEIVED_JPEG_TS;
            tryComplete();
        }

        public void setPreviewTimestamp(long timestamp) {
            if (DEBUG) {
                Log.d(TAG, "setPreviewTimestamp - called for request " + mRequest.getRequestId());
@@ -148,7 +201,8 @@ public class CaptureCollector {
            if (!needsJpeg) {
                if (!mHasStarted) {
                    mHasStarted = true;
                    CaptureCollector.this.mDeviceState.setCaptureStart(mRequest, mTimestamp);
                    CaptureCollector.this.mDeviceState.setCaptureStart(mRequest, mTimestamp,
                            CameraDeviceState.NO_CAPTURE_ERROR);
                }
            }

@@ -171,8 +225,37 @@ public class CaptureCollector {
            mReceivedFlags |= FLAG_RECEIVED_PREVIEW;
            tryComplete();
        }

        public void setPreviewFailed() {
            if (DEBUG) {
                Log.d(TAG, "setPreviewFailed - called for request " + mRequest.getRequestId());
            }
            if (!needsPreview || isPreviewCompleted()) {
                return;
            }
            mFailedPreview = true;

            mReceivedFlags |= FLAG_RECEIVED_PREVIEW;
            mReceivedFlags |= FLAG_RECEIVED_PREVIEW_TS;
            tryComplete();
        }

        // Comparison and equals based on frame number.
        @Override
        public int compareTo(CaptureHolder captureHolder) {
            return (mRequest.getFrameNumber() > captureHolder.mRequest.getFrameNumber()) ? 1 :
                    ((mRequest.getFrameNumber() == captureHolder.mRequest.getFrameNumber()) ? 0 :
                            -1);
        }

        // Comparison and equals based on frame number.
        @Override
        public boolean equals(Object o) {
            return o instanceof CaptureHolder && compareTo((CaptureHolder) o) == 0;
        }
    }

    private final TreeSet<CaptureHolder> mActiveRequests;
    private final ArrayDeque<CaptureHolder> mJpegCaptureQueue;
    private final ArrayDeque<CaptureHolder> mJpegProduceQueue;
    private final ArrayDeque<CaptureHolder> mPreviewCaptureQueue;
@@ -200,6 +283,7 @@ public class CaptureCollector {
        mJpegProduceQueue = new ArrayDeque<>(MAX_JPEGS_IN_FLIGHT);
        mPreviewCaptureQueue = new ArrayDeque<>(mMaxInFlight);
        mPreviewProduceQueue = new ArrayDeque<>(mMaxInFlight);
        mActiveRequests = new TreeSet<>();
        mIsEmpty = mLock.newCondition();
        mNotFull = mLock.newCondition();
        mPreviewsEmpty = mLock.newCondition();
@@ -263,7 +347,7 @@ public class CaptureCollector {
                mPreviewProduceQueue.add(h);
                mInFlightPreviews++;
            }

            mActiveRequests.add(h);

            mInFlight++;
            return true;
@@ -440,7 +524,9 @@ public class CaptureCollector {
        try {
            CaptureHolder h = mPreviewCaptureQueue.poll();
            if (h == null) {
                Log.w(TAG, "previewCaptured called with no preview request on queue!");
                if (DEBUG) {
                    Log.d(TAG, "previewCaptured called with no preview request on queue!");
                }
                return null;
            }
            h.setPreviewTimestamp(timestamp);
@@ -471,6 +557,81 @@ public class CaptureCollector {
        }
    }

    /**
     * Called to alert the {@link CaptureCollector} that the next pending preview capture has failed.
     */
    public void failNextPreview() {
        final ReentrantLock lock = this.mLock;
        lock.lock();
        try {
            CaptureHolder h1 = mPreviewCaptureQueue.peek();
            CaptureHolder h2 = mPreviewProduceQueue.peek();

            // Find the request with the lowest frame number.
            CaptureHolder h = (h1 == null) ? h2 :
                              ((h2 == null) ? h1 :
                              ((h1.compareTo(h2) <= 0) ? h1 :
                              h2));

            if (h != null) {
                mPreviewCaptureQueue.remove(h);
                mPreviewProduceQueue.remove(h);
                mActiveRequests.remove(h);
                h.setPreviewFailed();
            }
        } finally {
            lock.unlock();
        }
    }

    /**
     * Called to alert the {@link CaptureCollector} that the next pending jpeg capture has failed.
     */
    public void failNextJpeg() {
        final ReentrantLock lock = this.mLock;
        lock.lock();
        try {
            CaptureHolder h1 = mJpegCaptureQueue.peek();
            CaptureHolder h2 = mJpegProduceQueue.peek();

            // Find the request with the lowest frame number.
            CaptureHolder h = (h1 == null) ? h2 :
                              ((h2 == null) ? h1 :
                              ((h1.compareTo(h2) <= 0) ? h1 :
                              h2));

            if (h != null) {
                mJpegCaptureQueue.remove(h);
                mJpegProduceQueue.remove(h);
                mActiveRequests.remove(h);
                h.setJpegFailed();
            }
        } finally {
            lock.unlock();
        }
    }

    /**
     * Called to alert the {@link CaptureCollector} all pending captures have failed.
     */
    public void failAll() {
        final ReentrantLock lock = this.mLock;
        lock.lock();
        try {
            CaptureHolder h;
            while ((h = mActiveRequests.pollFirst()) != null) {
                h.setPreviewFailed();
                h.setJpegFailed();
            }
            mPreviewCaptureQueue.clear();
            mPreviewProduceQueue.clear();
            mJpegCaptureQueue.clear();
            mJpegProduceQueue.clear();
        } finally {
            lock.unlock();
        }
    }

    private void onPreviewCompleted() {
        mInFlightPreviews--;
        if (mInFlightPreviews < 0) {
@@ -496,6 +657,7 @@ public class CaptureCollector {
        }

        mCompletedRequests.add(capture);
        mActiveRequests.remove(capture);

        mNotFull.signalAll();
        if (mInFlight == 0) {
+72 −9

File changed.

Preview size limit exceeded, changes collapsed.

Loading