summaryrefslogtreecommitdiffstats
path: root/core/java/android/hardware/camera2
diff options
context:
space:
mode:
authorEino-Ville Talvala <etalvala@google.com>2014-08-06 14:31:08 -0700
committerEino-Ville Talvala <etalvala@google.com>2014-08-27 11:08:18 -0700
commitacc0095bc84914d3ce41ad8298f698c37935b8a8 (patch)
tree35fef13c48d2c2e28ca2ac94964325226c220c4d /core/java/android/hardware/camera2
parenta9bdc43ec2b8862db579aaced357184b7496468f (diff)
downloadframeworks_base-acc0095bc84914d3ce41ad8298f698c37935b8a8.zip
frameworks_base-acc0095bc84914d3ce41ad8298f698c37935b8a8.tar.gz
frameworks_base-acc0095bc84914d3ce41ad8298f698c37935b8a8.tar.bz2
Camera2: Correct error handling
- Report capture failures from service to application - Only go to error state for device-level errors - Adjust binder interface method names to match the service side names - Reduce failed session creation logging - Don't fire CaptureSession.onActive for CameraDevice.onBusy - Check with session to determine capture failure reason Bug: 17160301 Bug: 15524101 Bug: 14448494 Bug: 11272459 Change-Id: I9dd606004fd7845910dc865738fbe17f1640f07d
Diffstat (limited to 'core/java/android/hardware/camera2')
-rw-r--r--core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl4
-rw-r--r--core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java18
-rw-r--r--core/java/android/hardware/camera2/impl/CameraDeviceImpl.java107
-rw-r--r--core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java8
-rw-r--r--core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java4
5 files changed, 114 insertions, 27 deletions
diff --git a/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl b/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl
index caabed3..ca0935c 100644
--- a/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl
+++ b/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl
@@ -26,8 +26,8 @@ interface ICameraDeviceCallbacks
* Keep up-to-date with frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h
*/
- oneway void onCameraError(int errorCode, in CaptureResultExtras resultExtras);
- oneway void onCameraIdle();
+ oneway void onDeviceError(int errorCode, in CaptureResultExtras resultExtras);
+ oneway void onDeviceIdle();
oneway void onCaptureStarted(in CaptureResultExtras resultExtras, long timestamp);
oneway void onResultReceived(in CameraMetadataNative result,
in CaptureResultExtras resultExtras);
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index 621968b..9ca1fba 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -74,7 +74,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
private boolean mSkipUnconfigure = false;
/** Is the session in the process of aborting? Pay attention to BUSY->IDLE transitions. */
- private boolean mAborting;
+ private volatile boolean mAborting;
/**
* Create a new CameraCaptureSession.
@@ -346,6 +346,20 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
}
/**
+ * Whether currently in mid-abort.
+ *
+ * <p>This is used by the implementation to set the capture failure
+ * reason, in lieu of more accurate error codes from the camera service.
+ * Unsynchronized to avoid deadlocks between simultaneous session->device,
+ * device->session calls.</p>
+ *
+ * <p>Package-private.</p>
+ */
+ boolean isAborting() {
+ return mAborting;
+ }
+
+ /**
* Post calls into a CameraCaptureSession.StateListener to the user-specified {@code handler}.
*/
private StateListener createUserStateListenerProxy(Handler handler, StateListener listener) {
@@ -502,8 +516,8 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
// TODO: Queue captures during abort instead of failing them
// since the app won't be able to distinguish the two actives
+ // Don't signal the application since there's no clean mapping here
Log.w(TAG, "Device is now busy; do not submit new captures (TODO: allow this)");
- mStateListener.onActive(session);
}
@Override
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 79ce9df..513d222 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -384,7 +384,7 @@ public class CameraDeviceImpl extends CameraDevice {
catch (IllegalArgumentException e) {
// OK. camera service can reject stream config if it's not supported by HAL
// This is only the result of a programmer misusing the camera2 api.
- Log.e(TAG, "Stream configuration failed", e);
+ Log.w(TAG, "Stream configuration failed");
return false;
}
@@ -1097,31 +1097,51 @@ public class CameraDeviceImpl extends CameraDevice {
*/
static final int ERROR_CAMERA_SERVICE = 2;
+ /**
+ * Camera has encountered an error processing a single request.
+ */
+ 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;
+
+ /**
+ * Camera has encountered an error producing an image buffer for a single capture
+ */
+ static final int ERROR_CAMERA_BUFFER = 5;
+
@Override
public IBinder asBinder() {
return this;
}
@Override
- public void onCameraError(final int errorCode, CaptureResultExtras resultExtras) {
- Runnable r = null;
+ public void onDeviceError(final int errorCode, CaptureResultExtras resultExtras) {
+ if (DEBUG) {
+ Log.d(TAG, String.format(
+ "Device error received, code %d, frame number %d, request ID %d, subseq ID %d",
+ errorCode, resultExtras.getFrameNumber(), resultExtras.getRequestId(),
+ resultExtras.getSubsequenceId()));
+ }
synchronized(mInterfaceLock) {
if (mRemoteDevice == null) {
return; // Camera already closed
}
- mInError = true;
switch (errorCode) {
case ERROR_CAMERA_DISCONNECTED:
- r = mCallOnDisconnected;
+ CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected);
break;
default:
Log.e(TAG, "Unknown error from camera device: " + errorCode);
// no break
case ERROR_CAMERA_DEVICE:
case ERROR_CAMERA_SERVICE:
- r = new Runnable() {
+ mInError = true;
+ Runnable r = new Runnable() {
@Override
public void run() {
if (!CameraDeviceImpl.this.isClosed()) {
@@ -1129,21 +1149,19 @@ public class CameraDeviceImpl extends CameraDevice {
}
}
};
+ CameraDeviceImpl.this.mDeviceHandler.post(r);
+ break;
+ case ERROR_CAMERA_REQUEST:
+ case ERROR_CAMERA_RESULT:
+ case ERROR_CAMERA_BUFFER:
+ onCaptureErrorLocked(errorCode, resultExtras);
break;
}
- CameraDeviceImpl.this.mDeviceHandler.post(r);
-
- // Fire onCaptureSequenceCompleted
- if (DEBUG) {
- Log.v(TAG, String.format("got error frame %d", resultExtras.getFrameNumber()));
- }
- mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/true);
- checkAndFireSequenceComplete();
}
}
@Override
- public void onCameraIdle() {
+ public void onDeviceIdle() {
if (DEBUG) {
Log.d(TAG, "Camera now idle");
}
@@ -1246,7 +1264,6 @@ public class CameraDeviceImpl extends CameraDevice {
final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
-
Runnable resultDispatch = null;
// Either send a partial result or the final capture completed result
@@ -1290,11 +1307,67 @@ public class CameraDeviceImpl extends CameraDevice {
if (!isPartialResult) {
checkAndFireSequenceComplete();
}
+ }
+ }
+ /**
+ * Called by onDeviceError for handling single-capture failures.
+ */
+ private void onCaptureErrorLocked(int errorCode, CaptureResultExtras resultExtras) {
+
+ final int requestId = resultExtras.getRequestId();
+ final int subsequenceId = resultExtras.getSubsequenceId();
+ final long frameNumber = resultExtras.getFrameNumber();
+ final CaptureListenerHolder holder =
+ CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
+
+ final CaptureRequest request = holder.getRequest(subsequenceId);
+
+ // No way to report buffer errors right now
+ if (errorCode == ERROR_CAMERA_BUFFER) {
+ Log.e(TAG, String.format("Lost output buffer reported for frame %d", frameNumber));
+ return;
+ }
+
+ boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT);
+
+ // This is only approximate - exact handling needs the camera service and HAL to
+ // disambiguate between request failures to due abort and due to real errors.
+ // For now, assume that if the session believes we're mid-abort, then the error
+ // is due to abort.
+ int reason = (mCurrentSession != null && mCurrentSession.isAborting()) ?
+ CaptureFailure.REASON_FLUSHED :
+ CaptureFailure.REASON_ERROR;
+
+ final CaptureFailure failure = new CaptureFailure(
+ request,
+ reason,
+ /*dropped*/ mayHaveBuffers,
+ requestId,
+ frameNumber);
+
+ Runnable failureDispatch = new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()){
+ holder.getListener().onCaptureFailed(
+ CameraDeviceImpl.this,
+ request,
+ failure);
+ }
+ }
+ };
+ holder.getHandler().post(failureDispatch);
+
+ // Fire onCaptureSequenceCompleted if appropriate
+ if (DEBUG) {
+ Log.v(TAG, String.format("got error frame %d", frameNumber));
}
+ mFrameNumberTracker.updateTracker(frameNumber, /*error*/true);
+ checkAndFireSequenceComplete();
}
- }
+ } // public class CameraDeviceCallbacks
/**
* Default handler management.
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index 5cbf109..410934e 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -211,7 +211,7 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
}
@Override
- public void onCameraError(final int errorCode, final CaptureResultExtras resultExtras) {
+ public void onDeviceError(final int errorCode, final CaptureResultExtras resultExtras) {
Message msg = getHandler().obtainMessage(CAMERA_ERROR,
/*arg1*/ errorCode, /*arg2*/ 0,
/*obj*/ resultExtras);
@@ -219,7 +219,7 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
}
@Override
- public void onCameraIdle() {
+ public void onDeviceIdle() {
Message msg = getHandler().obtainMessage(CAMERA_IDLE);
getHandler().sendMessage(msg);
}
@@ -267,11 +267,11 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
case CAMERA_ERROR: {
int errorCode = msg.arg1;
CaptureResultExtras resultExtras = (CaptureResultExtras) msg.obj;
- mCallbacks.onCameraError(errorCode, resultExtras);
+ mCallbacks.onDeviceError(errorCode, resultExtras);
break;
}
case CAMERA_IDLE:
- mCallbacks.onCameraIdle();
+ mCallbacks.onDeviceIdle();
break;
case CAPTURE_STARTED: {
long timestamp = msg.arg2 & 0xFFFFFFFFL;
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index 1cf7797..ffc55f1 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -97,7 +97,7 @@ public class LegacyCameraDevice implements AutoCloseable {
Log.d(TAG, "doing onError callback.");
}
try {
- mDeviceCallbacks.onCameraError(errorCode, extras);
+ mDeviceCallbacks.onDeviceError(errorCode, extras);
} catch (RemoteException e) {
throw new IllegalStateException(
"Received remote exception during onCameraError callback: ", e);
@@ -125,7 +125,7 @@ public class LegacyCameraDevice implements AutoCloseable {
Log.d(TAG, "doing onIdle callback.");
}
try {
- mDeviceCallbacks.onCameraIdle();
+ mDeviceCallbacks.onDeviceIdle();
} catch (RemoteException e) {
throw new IllegalStateException(
"Received remote exception during onCameraIdle callback: ", e);