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;
}