Merge "Camera2: Invoke onError callbacks for failure to open" into lmp-preview-dev

This commit is contained in:
Eino-Ville Talvala
2014-06-05 01:08:53 +00:00
committed by Android (Google) Code Review
2 changed files with 86 additions and 12 deletions

View File

@@ -226,7 +226,7 @@ public final class CameraManager {
synchronized (mLock) {
ICameraDeviceUser cameraUser;
ICameraDeviceUser cameraUser = null;
android.hardware.camera2.impl.CameraDeviceImpl deviceImpl =
new android.hardware.camera2.impl.CameraDeviceImpl(
@@ -248,8 +248,23 @@ public final class CameraManager {
// Use legacy camera implementation for HAL1 devices
Log.i(TAG, "Using legacy camera HAL.");
cameraUser = CameraDeviceUserShim.connectBinderShim(callbacks, id);
} else if (e.getReason() == CameraAccessException.CAMERA_IN_USE ||
e.getReason() == CameraAccessException.MAX_CAMERAS_IN_USE ||
e.getReason() == CameraAccessException.CAMERA_DISABLED ||
e.getReason() == CameraAccessException.CAMERA_DISCONNECTED ||
e.getReason() == CameraAccessException.CAMERA_ERROR) {
// Received one of the known connection errors
// The remote camera device cannot be connected to, so
// set the local camera to the startup error state
deviceImpl.setRemoteFailure(e);
if (e.getReason() == CameraAccessException.CAMERA_DISABLED ||
e.getReason() == CameraAccessException.CAMERA_DISCONNECTED) {
// Per API docs, these failures call onError and throw
throw e;
}
} else {
// Rethrow otherwise
// Unexpected failure - rethrow
throw e;
}
}
@@ -299,7 +314,7 @@ public final class CameraManager {
*
* <p>If opening the camera device fails, then the device listener's
* {@link CameraDevice.StateListener#onError onError} method will be called, and subsequent
* calls on the camera device will throw an {@link IllegalStateException}.</p>
* calls on the camera device will throw a {@link CameraAccessException}.</p>
*
* @param cameraId
* The unique identifier of the camera device to open

View File

@@ -64,6 +64,7 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice {
private volatile StateListener mSessionStateListener;
private final Handler mDeviceHandler;
private boolean mInError = false;
private boolean mIdle = true;
/** map request IDs to listener/request data */
@@ -211,6 +212,9 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice {
public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
// TODO: Move from decorator to direct binder-mediated exceptions
synchronized(mLock) {
// If setRemoteFailure already called, do nothing
if (mInError) return;
mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice);
mDeviceHandler.post(mCallOnOpened);
@@ -218,6 +222,52 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice {
}
}
/**
* Call to indicate failed connection to a remote camera device.
*
* <p>This places the camera device in the error state and informs the listener.
* Use in place of setRemoteDevice() when startup fails.</p>
*/
public void setRemoteFailure(final CameraRuntimeException failure) {
int failureCode = StateListener.ERROR_CAMERA_DEVICE;
boolean failureIsError = true;
switch (failure.getReason()) {
case CameraAccessException.CAMERA_IN_USE:
failureCode = StateListener.ERROR_CAMERA_IN_USE;
break;
case CameraAccessException.MAX_CAMERAS_IN_USE:
failureCode = StateListener.ERROR_MAX_CAMERAS_IN_USE;
break;
case CameraAccessException.CAMERA_DISABLED:
failureCode = StateListener.ERROR_CAMERA_DISABLED;
break;
case CameraAccessException.CAMERA_DISCONNECTED:
failureIsError = false;
break;
case CameraAccessException.CAMERA_ERROR:
failureCode = StateListener.ERROR_CAMERA_DEVICE;
break;
default:
Log.wtf(TAG, "Unknown failure in opening camera device: " + failure.getReason());
break;
}
final int code = failureCode;
final boolean isError = failureIsError;
synchronized (mLock) {
mInError = true;
mDeviceHandler.post(new Runnable() {
public void run() {
if (isError) {
mDeviceListener.onError(CameraDeviceImpl.this, code);
} else {
mDeviceListener.onDisconnected(CameraDeviceImpl.this);
}
}
});
}
}
@Override
public String getId() {
return mCameraId;
@@ -230,7 +280,7 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice {
outputs = new ArrayList<Surface>();
}
synchronized (mLock) {
checkIfCameraClosed();
checkIfCameraClosedOrInError();
HashSet<Surface> addSet = new HashSet<Surface>(outputs); // Streams to create
List<Integer> deleteList = new ArrayList<Integer>(); // Streams to delete
@@ -298,7 +348,7 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice {
Log.d(TAG, "createCaptureSession");
}
checkIfCameraClosed();
checkIfCameraClosedOrInError();
// TODO: we must be in UNCONFIGURED mode to begin with, or using another session
@@ -336,7 +386,7 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice {
public CaptureRequest.Builder createCaptureRequest(int templateType)
throws CameraAccessException {
synchronized (mLock) {
checkIfCameraClosed();
checkIfCameraClosedOrInError();
CameraMetadataNative templatedRequest = new CameraMetadataNative();
@@ -456,7 +506,7 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice {
}
synchronized (mLock) {
checkIfCameraClosed();
checkIfCameraClosedOrInError();
int requestId;
if (repeating) {
@@ -528,7 +578,7 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice {
public void stopRepeating() throws CameraAccessException {
synchronized (mLock) {
checkIfCameraClosed();
checkIfCameraClosedOrInError();
if (mRepeatingRequestId != REQUEST_ID_NONE) {
int requestId = mRepeatingRequestId;
@@ -559,7 +609,7 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice {
private void waitUntilIdle() throws CameraAccessException {
synchronized (mLock) {
checkIfCameraClosed();
checkIfCameraClosedOrInError();
if (mRepeatingRequestId != REQUEST_ID_NONE) {
throw new IllegalStateException("Active repeating request ongoing");
}
@@ -580,7 +630,7 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice {
@Override
public void flush() throws CameraAccessException {
synchronized (mLock) {
checkIfCameraClosed();
checkIfCameraClosedOrInError();
mDeviceHandler.post(mCallOnBusy);
try {
@@ -614,11 +664,15 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice {
// impossible
}
if (mRemoteDevice != null) {
// Only want to fire the onClosed callback once;
// either a normal close where the remote device is valid
// or a close after a startup error (no remote device but in error state)
if (mRemoteDevice != null || mInError) {
mDeviceHandler.post(mCallOnClosed);
}
mRemoteDevice = null;
mInError = false;
}
}
@@ -835,6 +889,7 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice {
if (isClosed()) return;
synchronized(mLock) {
mInError = true;
switch (errorCode) {
case ERROR_CAMERA_DISCONNECTED:
r = mCallOnDisconnected;
@@ -1032,7 +1087,11 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice {
return handler;
}
private void checkIfCameraClosed() {
private void checkIfCameraClosedOrInError() throws CameraAccessException {
if (mInError) {
throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
"The camera device has encountered a serious error");
}
if (mRemoteDevice == null) {
throw new IllegalStateException("CameraDevice was already closed");
}