am 979c03a2: Merge "camera2: Avoid spurious IDLE transitions." into lmp-dev

* commit '979c03a21bc346ddbfbfcf5d1d4239e5cdf30302':
  camera2: Avoid spurious IDLE transitions.
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
index 6aab53c..e96c15f7 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
@@ -16,8 +16,8 @@
 
 package android.hardware.camera2.legacy;
 
+import android.hardware.camera2.impl.CameraDeviceImpl;
 import android.hardware.camera2.impl.CameraMetadataNative;
-import android.hardware.camera2.utils.CameraBinderDecorator;
 import android.os.Handler;
 import android.util.Log;
 
@@ -53,7 +53,7 @@
             "CAPTURING"};
 
     private int mCurrentState = STATE_UNCONFIGURED;
-    private int mCurrentError = CameraBinderDecorator.NO_ERROR;
+    private int mCurrentError = NO_CAPTURE_ERROR;
 
     private RequestHolder mCurrentRequest = null;
 
@@ -87,7 +87,7 @@
      * </p>
      *
      * @param error the error to set.  Should be one of the error codes defined in
-     *      {@link android.hardware.camera2.utils.CameraBinderDecorator}.
+     *      {@link CameraDeviceImpl.CameraDeviceCallbacks}.
      */
     public synchronized void setError(int error) {
         mCurrentError = error;
@@ -102,11 +102,11 @@
      * {@link CameraDeviceStateListener#onConfiguring()} will be called.
      * </p>
      *
-     * @return {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
+     * @return {@code false} if an error has occurred.
      */
-    public synchronized int setConfiguring() {
+    public synchronized boolean setConfiguring() {
         doStateTransition(STATE_CONFIGURING);
-        return mCurrentError;
+        return mCurrentError == NO_CAPTURE_ERROR;
     }
 
     /**
@@ -117,11 +117,11 @@
      * {@link CameraDeviceStateListener#onIdle()} will be called.
      * </p>
      *
-     * @return {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
+     * @return {@code false} if an error has occurred.
      */
-    public synchronized int setIdle() {
+    public synchronized boolean setIdle() {
         doStateTransition(STATE_IDLE);
-        return mCurrentError;
+        return mCurrentError == NO_CAPTURE_ERROR;
     }
 
     /**
@@ -137,13 +137,13 @@
      * @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.
+     * @return {@code false} if an error has occurred.
      */
-    public synchronized int setCaptureStart(final RequestHolder request, long timestamp,
+    public synchronized boolean setCaptureStart(final RequestHolder request, long timestamp,
                                             int captureError) {
         mCurrentRequest = request;
         doStateTransition(STATE_CAPTURING, timestamp, captureError);
-        return mCurrentError;
+        return mCurrentError == NO_CAPTURE_ERROR;
     }
 
     /**
@@ -161,16 +161,16 @@
      * @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.
+     * @return {@code false} if an error has occurred.
      */
-    public synchronized int setCaptureResult(final RequestHolder request,
+    public synchronized boolean setCaptureResult(final RequestHolder request,
                                              final CameraMetadataNative result,
                                              final int captureError) {
         if (mCurrentState != STATE_CAPTURING) {
             Log.e(TAG, "Cannot receive result while in state: " + mCurrentState);
-            mCurrentError = CameraBinderDecorator.INVALID_OPERATION;
+            mCurrentError = CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE;
             doStateTransition(STATE_ERROR);
-            return mCurrentError;
+            return mCurrentError == NO_CAPTURE_ERROR;
         }
 
         if (mCurrentHandler != null && mCurrentListener != null) {
@@ -190,7 +190,7 @@
                 });
             }
         }
-        return mCurrentError;
+        return mCurrentError == NO_CAPTURE_ERROR;
     }
 
     /**
@@ -206,7 +206,7 @@
     }
 
     private void doStateTransition(int newState) {
-        doStateTransition(newState, /*timestamp*/0, CameraBinderDecorator.NO_ERROR);
+        doStateTransition(newState, /*timestamp*/0, NO_CAPTURE_ERROR);
     }
 
     private void doStateTransition(int newState, final long timestamp, final int error) {
@@ -233,7 +233,7 @@
             case STATE_CONFIGURING:
                 if (mCurrentState != STATE_UNCONFIGURED && mCurrentState != STATE_IDLE) {
                     Log.e(TAG, "Cannot call configure while in state: " + mCurrentState);
-                    mCurrentError = CameraBinderDecorator.INVALID_OPERATION;
+                    mCurrentError = CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE;
                     doStateTransition(STATE_ERROR);
                     break;
                 }
@@ -255,7 +255,7 @@
 
                 if (mCurrentState != STATE_CONFIGURING && mCurrentState != STATE_CAPTURING) {
                     Log.e(TAG, "Cannot call idle while in state: " + mCurrentState);
-                    mCurrentError = CameraBinderDecorator.INVALID_OPERATION;
+                    mCurrentError = CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE;
                     doStateTransition(STATE_ERROR);
                     break;
                 }
@@ -274,7 +274,7 @@
             case STATE_CAPTURING:
                 if (mCurrentState != STATE_IDLE && mCurrentState != STATE_CAPTURING) {
                     Log.e(TAG, "Cannot call capture while in state: " + mCurrentState);
-                    mCurrentError = CameraBinderDecorator.INVALID_OPERATION;
+                    mCurrentError = CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE;
                     doStateTransition(STATE_ERROR);
                     break;
                 }
diff --git a/core/java/android/hardware/camera2/legacy/GLThreadManager.java b/core/java/android/hardware/camera2/legacy/GLThreadManager.java
index 2c584ef..c8e0147 100644
--- a/core/java/android/hardware/camera2/legacy/GLThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/GLThreadManager.java
@@ -17,6 +17,7 @@
 package android.hardware.camera2.legacy;
 
 import android.graphics.SurfaceTexture;
+import android.hardware.camera2.impl.CameraDeviceImpl;
 import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.Message;
@@ -42,6 +43,8 @@
 
     private CaptureCollector mCaptureCollector;
 
+    private final CameraDeviceState mDeviceState;
+
     private final SurfaceTextureRenderer mTextureRenderer;
 
     private final RequestHandlerThread mGLHandlerThread;
@@ -76,42 +79,47 @@
             if (mCleanup) {
                 return true;
             }
-            switch (msg.what) {
-                case MSG_NEW_CONFIGURATION:
-                    ConfigureHolder configure = (ConfigureHolder) msg.obj;
-                    mTextureRenderer.cleanupEGLContext();
-                    mTextureRenderer.configureSurfaces(configure.surfaces);
-                    mCaptureCollector = checkNotNull(configure.collector);
-                    configure.condition.open();
-                    mConfigured = true;
-                    break;
-                case MSG_NEW_FRAME:
-                    if (mDroppingFrames) {
-                        Log.w(TAG, "Ignoring frame.");
+            try {
+                switch (msg.what) {
+                    case MSG_NEW_CONFIGURATION:
+                        ConfigureHolder configure = (ConfigureHolder) msg.obj;
+                        mTextureRenderer.cleanupEGLContext();
+                        mTextureRenderer.configureSurfaces(configure.surfaces);
+                        mCaptureCollector = checkNotNull(configure.collector);
+                        configure.condition.open();
+                        mConfigured = true;
                         break;
-                    }
-                    if (DEBUG) {
-                        mPrevCounter.countAndLog();
-                    }
-                    if (!mConfigured) {
-                        Log.e(TAG, "Dropping frame, EGL context not configured!");
-                    }
-                    mTextureRenderer.drawIntoSurfaces(mCaptureCollector);
-                    break;
-                case MSG_CLEANUP:
-                    mTextureRenderer.cleanupEGLContext();
-                    mCleanup = true;
-                    mConfigured = false;
-                    break;
-                case MSG_DROP_FRAMES:
-                    mDroppingFrames = true;
-                    break;
-                case MSG_ALLOW_FRAMES:
-                    mDroppingFrames = false;
-                    break;
-                default:
-                    Log.e(TAG, "Unhandled message " + msg.what + " on GLThread.");
-                    break;
+                    case MSG_NEW_FRAME:
+                        if (mDroppingFrames) {
+                            Log.w(TAG, "Ignoring frame.");
+                            break;
+                        }
+                        if (DEBUG) {
+                            mPrevCounter.countAndLog();
+                        }
+                        if (!mConfigured) {
+                            Log.e(TAG, "Dropping frame, EGL context not configured!");
+                        }
+                        mTextureRenderer.drawIntoSurfaces(mCaptureCollector);
+                        break;
+                    case MSG_CLEANUP:
+                        mTextureRenderer.cleanupEGLContext();
+                        mCleanup = true;
+                        mConfigured = false;
+                        break;
+                    case MSG_DROP_FRAMES:
+                        mDroppingFrames = true;
+                        break;
+                    case MSG_ALLOW_FRAMES:
+                        mDroppingFrames = false;
+                        break;
+                    default:
+                        Log.e(TAG, "Unhandled message " + msg.what + " on GLThread.");
+                        break;
+                }
+            } catch (Exception e) {
+                Log.e(TAG, "Received exception on GL render thread: ", e);
+                mDeviceState.setError(CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
             }
             return true;
         }
@@ -122,11 +130,13 @@
      *
      * @param cameraId the camera id for this thread.
      * @param facing direction the camera is facing.
+     * @param state {@link CameraDeviceState} to use for error handling.
      */
-    public GLThreadManager(int cameraId, int facing) {
+    public GLThreadManager(int cameraId, int facing, CameraDeviceState state) {
         mTextureRenderer = new SurfaceTextureRenderer(facing);
         TAG = String.format("CameraDeviceGLThread-%d", cameraId);
         mGLHandlerThread = new RequestHandlerThread(TAG, mGLHandlerCb);
+        mDeviceState = state;
     }
 
     /**
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index 9143152..a724b41 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -26,6 +26,7 @@
 import android.hardware.camera2.params.StreamConfiguration;
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.hardware.camera2.utils.ArrayUtils;
+import android.hardware.camera2.utils.CameraBinderDecorator;
 import android.hardware.camera2.utils.LongParcelable;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.utils.CameraRuntimeException;
@@ -288,17 +289,18 @@
             }
         }
 
-        int error = mDeviceState.setConfiguring();
-        if (error == NO_ERROR) {
+        boolean success = false;
+        if (mDeviceState.setConfiguring()) {
             mRequestThreadManager.configure(outputs);
-            error = mDeviceState.setIdle();
+            success = mDeviceState.setIdle();
         }
 
-        if (error == NO_ERROR) {
+        if (success) {
             mConfiguredSurfaces = outputs != null ? new ArrayList<>(outputs) : null;
+        } else {
+            return CameraBinderDecorator.INVALID_OPERATION;
         }
-
-        return error;
+        return CameraBinderDecorator.NO_ERROR;
     }
 
     /**
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index 87ee1ee..788b6d8 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -98,6 +98,7 @@
     private SurfaceTexture mDummyTexture;
     private Surface mDummySurface;
 
+    private final Object mIdleLock = new Object();
     private final FpsCounter mPrevCounter = new FpsCounter("Incoming Preview");
     private final FpsCounter mRequestCounter = new FpsCounter("Incoming Requests");
 
@@ -173,6 +174,14 @@
         }
     }
 
+    private final Camera.ErrorCallback mErrorCallback = new Camera.ErrorCallback() {
+        @Override
+        public void onError(int i, Camera camera) {
+            Log.e(TAG, "Received error " + i + " from the Camera1 ErrorCallback");
+            mDeviceState.setError(CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
+        }
+    };
+
     private final ConditionVariable mReceivedJpeg = new ConditionVariable(false);
 
     private final Camera.PictureCallback mJpegCallback = new Camera.PictureCallback() {
@@ -405,7 +414,7 @@
 
         // TODO: Detect and optimize single-output paths here to skip stream teeing.
         if (mGLThreadManager == null) {
-            mGLThreadManager = new GLThreadManager(mCameraId, facing);
+            mGLThreadManager = new GLThreadManager(mCameraId, facing, mDeviceState);
             mGLThreadManager.start();
         }
         mGLThreadManager.waitUntilStarted();
@@ -617,9 +626,20 @@
                                     CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
                             break;
                         }
-                        mDeviceState.setIdle();
-                        break;
-                    } else {
+
+                        synchronized (mIdleLock) {
+                            // Retry the the request queue.
+                            nextBurst = mRequestQueue.getNext();
+
+                            // If we still have no queued requests, go idle.
+                            if (nextBurst == null) {
+                                mDeviceState.setIdle();
+                                break;
+                            }
+                        }
+                    }
+
+                    if (nextBurst != null) {
                         // Queue another capture if we did not get the last burst.
                         handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST);
                     }
@@ -831,6 +851,7 @@
         mFaceDetectMapper = new LegacyFaceDetectMapper(mCamera, mCharacteristics);
         mCaptureCollector = new CaptureCollector(MAX_IN_FLIGHT_REQUESTS, mDeviceState);
         mRequestThread = new RequestHandlerThread(name, mRequestHandlerCb);
+        mCamera.setErrorCallback(mErrorCallback);
     }
 
     /**
@@ -883,8 +904,11 @@
     public int submitCaptureRequests(List<CaptureRequest> requests, boolean repeating,
             /*out*/LongParcelable frameNumber) {
         Handler handler = mRequestThread.waitAndGetHandler();
-        int ret = mRequestQueue.submit(requests, repeating, frameNumber);
-        handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST);
+        int ret;
+        synchronized (mIdleLock) {
+            ret = mRequestQueue.submit(requests, repeating, frameNumber);
+            handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST);
+        }
         return ret;
     }