summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java449
-rw-r--r--core/java/android/hardware/camera2/impl/CameraDeviceImpl.java2
-rw-r--r--core/java/android/hardware/camera2/utils/CameraBinderDecorator.java31
-rw-r--r--core/java/android/hardware/camera2/utils/CameraServiceBinderDecorator.java70
4 files changed, 375 insertions, 177 deletions
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 9a3d806..a4a1559 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -23,7 +23,7 @@ import android.hardware.CameraInfo;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.legacy.CameraDeviceUserShim;
import android.hardware.camera2.legacy.LegacyMetadataMapper;
-import android.hardware.camera2.utils.CameraBinderDecorator;
+import android.hardware.camera2.utils.CameraServiceBinderDecorator;
import android.hardware.camera2.utils.CameraRuntimeException;
import android.hardware.camera2.utils.BinderHolder;
import android.os.IBinder;
@@ -52,6 +52,7 @@ import java.util.ArrayList;
public final class CameraManager {
private static final String TAG = "CameraManager";
+ private final boolean DEBUG;
/**
* This should match the ICameraService definition
@@ -63,7 +64,9 @@ public final class CameraManager {
private static final int API_VERSION_1 = 1;
private static final int API_VERSION_2 = 2;
- private final ICameraService mCameraService;
+ // Access only through getCameraServiceLocked to deal with binder death
+ private ICameraService mCameraService;
+
private ArrayList<String> mDeviceIdList;
private final ArrayMap<AvailabilityListener, Handler> mListenerMap =
@@ -72,35 +75,17 @@ public final class CameraManager {
private final Context mContext;
private final Object mLock = new Object();
+ private final CameraServiceListener mServiceListener = new CameraServiceListener();
+
/**
* @hide
*/
public CameraManager(Context context) {
- mContext = context;
-
- IBinder cameraServiceBinder = ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME);
- ICameraService cameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
-
- /**
- * Wrap the camera service in a decorator which automatically translates return codes
- * into exceptions, and RemoteExceptions into other exceptions.
- */
- mCameraService = CameraBinderDecorator.newInstance(cameraServiceRaw);
-
- try {
- CameraBinderDecorator.throwOnError(
- CameraMetadataNative.nativeSetupGlobalVendorTagDescriptor());
- } catch (CameraRuntimeException e) {
- handleRecoverableSetupErrors(e, "Failed to set up vendor tags");
- }
+ DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ synchronized(mLock) {
+ mContext = context;
- try {
- mCameraService.addListener(new CameraServiceListener());
- } catch(CameraRuntimeException e) {
- throw new IllegalStateException("Failed to register a camera service listener",
- e.asChecked());
- } catch (RemoteException e) {
- // impossible
+ connectCameraServiceLocked();
}
}
@@ -116,13 +101,9 @@ public final class CameraManager {
*/
public String[] getCameraIdList() throws CameraAccessException {
synchronized (mLock) {
- try {
- return getOrCreateDeviceIdListLocked().toArray(new String[0]);
- } catch(CameraAccessException e) {
- // this should almost never happen, except if mediaserver crashes
- throw new IllegalStateException(
- "Failed to query camera service for device ID list", e);
- }
+ // ID list creation handles various known failures in device enumeration, so only
+ // exceptions it'll throw are unexpected, and should be propagated upward.
+ return getOrCreateDeviceIdListLocked().toArray(new String[0]);
}
}
@@ -132,6 +113,9 @@ public final class CameraManager {
* <p>Registering the same listener again will replace the handler with the
* new one provided.</p>
*
+ * <p>The first time a listener is registered, it is immediately called
+ * with the availability status of all currently known camera devices.</p>
+ *
* @param listener The new listener to send camera availability notices to
* @param handler The handler on which the listener should be invoked, or
* {@code null} to use the current thread's {@link android.os.Looper looper}.
@@ -147,10 +131,11 @@ public final class CameraManager {
}
synchronized (mLock) {
- mListenerMap.put(listener, handler);
-
- // TODO: fire the current oldest known state when adding a new listener
- // (must be done while holding lock)
+ Handler oldHandler = mListenerMap.put(listener, handler);
+ // For new listeners, provide initial availability information
+ if (oldHandler == null) {
+ mServiceListener.updateListenerLocked(listener, handler);
+ }
}
}
@@ -176,64 +161,67 @@ public final class CameraManager {
* @return The properties of the given camera
*
* @throws IllegalArgumentException if the cameraId does not match any
- * currently connected camera device.
- * @throws CameraAccessException if the camera is disabled by device policy.
+ * known camera device.
+ * @throws CameraAccessException if the camera is disabled by device policy, or
+ * the camera device has been disconnected.
* @throws SecurityException if the application does not have permission to
- * access the camera
+ * access the camera
*
* @see #getCameraIdList
* @see android.app.admin.DevicePolicyManager#setCameraDisabled
*/
public CameraCharacteristics getCameraCharacteristics(String cameraId)
throws CameraAccessException {
+ CameraCharacteristics characteristics = null;
synchronized (mLock) {
if (!getOrCreateDeviceIdListLocked().contains(cameraId)) {
throw new IllegalArgumentException(String.format("Camera id %s does not match any" +
" currently connected camera device", cameraId));
}
- }
- int id = Integer.valueOf(cameraId);
+ int id = Integer.valueOf(cameraId);
- /*
- * Get the camera characteristics from the camera service directly if it supports it,
- * otherwise get them from the legacy shim instead.
- */
+ /*
+ * Get the camera characteristics from the camera service directly if it supports it,
+ * otherwise get them from the legacy shim instead.
+ */
- if (!supportsCamera2Api(cameraId)) {
- // Legacy backwards compatibility path; build static info from the camera parameters
- String[] outParameters = new String[1];
+ ICameraService cameraService = getCameraServiceLocked();
+ if (cameraService == null) {
+ throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
+ "Camera service is currently unavailable");
+ }
try {
- mCameraService.getLegacyParameters(id, /*out*/outParameters);
- String parameters = outParameters[0];
+ if (!supportsCamera2ApiLocked(cameraId)) {
+ // Legacy backwards compatibility path; build static info from the camera
+ // parameters
+ String[] outParameters = new String[1];
- CameraInfo info = new CameraInfo();
- mCameraService.getCameraInfo(id, /*out*/info);
+ cameraService.getLegacyParameters(id, /*out*/outParameters);
+ String parameters = outParameters[0];
- return LegacyMetadataMapper.createCharacteristics(parameters, info);
- } catch (RemoteException e) {
- // Impossible
- return null;
- } catch (CameraRuntimeException e) {
- throw e.asChecked();
- }
+ CameraInfo info = new CameraInfo();
+ cameraService.getCameraInfo(id, /*out*/info);
- } else {
- // Normal path: Get the camera characteristics directly from the camera service
- CameraMetadataNative info = new CameraMetadataNative();
+ characteristics = LegacyMetadataMapper.createCharacteristics(parameters, info);
+ } else {
+ // Normal path: Get the camera characteristics directly from the camera service
+ CameraMetadataNative info = new CameraMetadataNative();
- try {
- mCameraService.getCameraCharacteristics(id, info);
- } catch(CameraRuntimeException e) {
+ cameraService.getCameraCharacteristics(id, info);
+
+ characteristics = new CameraCharacteristics(info);
+ }
+ } catch (CameraRuntimeException e) {
throw e.asChecked();
- } catch(RemoteException e) {
- // impossible
- return null;
+ } catch (RemoteException e) {
+ // Camera service died - act as if the camera was disconnected
+ throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
+ "Camera service is currently unavailable", e);
}
-
- return new CameraCharacteristics(info);
}
+ return characteristics;
}
/**
@@ -278,10 +266,16 @@ public final class CameraManager {
ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();
int id = Integer.parseInt(cameraId);
try {
- if (supportsCamera2Api(cameraId)) {
+ if (supportsCamera2ApiLocked(cameraId)) {
// Use cameraservice's cameradeviceclient implementation for HAL3.2+ devices
- mCameraService.connectDevice(callbacks, id, mContext.getPackageName(),
- USE_CALLING_UID, holder);
+ ICameraService cameraService = getCameraServiceLocked();
+ if (cameraService == null) {
+ throw new CameraRuntimeException(
+ CameraAccessException.CAMERA_DISCONNECTED,
+ "Camera service is currently unavailable");
+ }
+ cameraService.connectDevice(callbacks, id,
+ mContext.getPackageName(), USE_CALLING_UID, holder);
cameraUser = ICameraDeviceUser.Stub.asInterface(holder.getBinder());
} else {
// Use legacy camera implementation for HAL1 devices
@@ -304,12 +298,19 @@ public final class CameraManager {
if (e.getReason() == CameraAccessException.CAMERA_DISABLED ||
e.getReason() == CameraAccessException.CAMERA_DISCONNECTED) {
// Per API docs, these failures call onError and throw
- throw e;
+ throw e.asChecked();
}
} else {
// Unexpected failure - rethrow
throw e;
}
+ } catch (RemoteException e) {
+ // Camera service died - act as if it's a CAMERA_DISCONNECTED case
+ CameraRuntimeException ce = new CameraRuntimeException(
+ CameraAccessException.CAMERA_DISCONNECTED,
+ "Camera service is currently unavailable", e);
+ deviceImpl.setRemoteFailure(ce);
+ throw ce.asChecked();
}
// TODO: factor out listener to be non-nested, then move setter to constructor
@@ -324,8 +325,6 @@ public final class CameraManager {
+ cameraId);
} catch (CameraRuntimeException e) {
throw e.asChecked();
- } catch (RemoteException e) {
- // impossible
}
return device;
}
@@ -444,27 +443,38 @@ public final class CameraManager {
}
}
+ /**
+ * Return or create the list of currently connected camera devices.
+ *
+ * <p>In case of errors connecting to the camera service, will return an empty list.</p>
+ */
private ArrayList<String> getOrCreateDeviceIdListLocked() throws CameraAccessException {
if (mDeviceIdList == null) {
int numCameras = 0;
+ ICameraService cameraService = getCameraServiceLocked();
+ ArrayList<String> deviceIdList = new ArrayList<>();
+
+ // If no camera service, then no devices
+ if (cameraService == null) {
+ return deviceIdList;
+ }
try {
- numCameras = mCameraService.getNumberOfCameras();
+ numCameras = cameraService.getNumberOfCameras();
} catch(CameraRuntimeException e) {
throw e.asChecked();
} catch (RemoteException e) {
- // impossible
- return null;
+ // camera service just died - if no camera service, then no devices
+ return deviceIdList;
}
- mDeviceIdList = new ArrayList<String>();
CameraMetadataNative info = new CameraMetadataNative();
for (int i = 0; i < numCameras; ++i) {
// Non-removable cameras use integers starting at 0 for their
// identifiers
boolean isDeviceSupported = false;
try {
- mCameraService.getCameraCharacteristics(i, info);
+ cameraService.getCameraCharacteristics(i, info);
if (!info.isEmpty()) {
isDeviceSupported = true;
} else {
@@ -474,16 +484,26 @@ public final class CameraManager {
// Got a BAD_VALUE from service, meaning that this
// device is not supported.
} catch(CameraRuntimeException e) {
- throw e.asChecked();
+ // DISCONNECTED means that the HAL reported an low-level error getting the
+ // device info; skip listing the device. Other errors,
+ // propagate exception onward
+ if (e.getReason() != CameraAccessException.CAMERA_DISCONNECTED) {
+ throw e.asChecked();
+ }
} catch(RemoteException e) {
- // impossible
+ // Camera service died - no devices to list
+ deviceIdList.clear();
+ return deviceIdList;
}
if (isDeviceSupported) {
- mDeviceIdList.add(String.valueOf(i));
+ deviceIdList.add(String.valueOf(i));
+ } else {
+ Log.w(TAG, "Error querying camera device " + i + " for listing.");
}
- }
+ }
+ mDeviceIdList = deviceIdList;
}
return mDeviceIdList;
}
@@ -506,8 +526,8 @@ public final class CameraManager {
* @param cameraId a non-{@code null} camera identifier
* @return {@code false} if the legacy shim needs to be used, {@code true} otherwise.
*/
- private boolean supportsCamera2Api(String cameraId) {
- return supportsCameraApi(cameraId, API_VERSION_2);
+ private boolean supportsCamera2ApiLocked(String cameraId) {
+ return supportsCameraApiLocked(cameraId, API_VERSION_2);
}
/**
@@ -517,33 +537,125 @@ public final class CameraManager {
* @param apiVersion the version, i.e. {@code API_VERSION_1} or {@code API_VERSION_2}
* @return {@code true} if connecting will work for that device version.
*/
- private boolean supportsCameraApi(String cameraId, int apiVersion) {
+ private boolean supportsCameraApiLocked(String cameraId, int apiVersion) {
int id = Integer.parseInt(cameraId);
/*
* Possible return values:
- * - NO_ERROR => Camera2 API is supported
- * - CAMERA_DEPRECATED_HAL => Camera2 API is *not* supported (thrown as an exception)
+ * - NO_ERROR => CameraX API is supported
+ * - CAMERA_DEPRECATED_HAL => CameraX API is *not* supported (thrown as an exception)
+ * - Remote exception => If the camera service died
*
* Anything else is an unexpected error we don't want to recover from.
*/
-
try {
- int res = mCameraService.supportsCameraApi(id, apiVersion);
+ ICameraService cameraService = getCameraServiceLocked();
+ // If no camera service, no support
+ if (cameraService == null) return false;
+
+ int res = cameraService.supportsCameraApi(id, apiVersion);
- if (res != CameraBinderDecorator.NO_ERROR) {
+ if (res != CameraServiceBinderDecorator.NO_ERROR) {
throw new AssertionError("Unexpected value " + res);
}
-
return true;
} catch (CameraRuntimeException e) {
- if (e.getReason() == CameraAccessException.CAMERA_DEPRECATED_HAL) {
- return false;
- } else {
+ if (e.getReason() != CameraAccessException.CAMERA_DEPRECATED_HAL) {
throw e;
}
+ // API level is not supported
} catch (RemoteException e) {
- throw new AssertionError("Camera service unreachable", e);
+ // Camera service is now down, no support for any API level
+ }
+ return false;
+ }
+
+ /**
+ * Connect to the camera service if it's available, and set up listeners.
+ *
+ * <p>Sets mCameraService to a valid pointer or null if the connection does not succeed.</p>
+ */
+ private void connectCameraServiceLocked() {
+ mCameraService = null;
+ IBinder cameraServiceBinder = ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME);
+ if (cameraServiceBinder == null) {
+ // Camera service is now down, leave mCameraService as null
+ return;
+ }
+ try {
+ cameraServiceBinder.linkToDeath(new CameraServiceDeathListener(), /*flags*/ 0);
+ } catch (RemoteException e) {
+ // Camera service is now down, leave mCameraService as null
+ return;
+ }
+
+ ICameraService cameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
+
+ /**
+ * Wrap the camera service in a decorator which automatically translates return codes
+ * into exceptions.
+ */
+ ICameraService cameraService = CameraServiceBinderDecorator.newInstance(cameraServiceRaw);
+
+ try {
+ CameraServiceBinderDecorator.throwOnError(
+ CameraMetadataNative.nativeSetupGlobalVendorTagDescriptor());
+ } catch (CameraRuntimeException e) {
+ handleRecoverableSetupErrors(e, "Failed to set up vendor tags");
+ }
+
+ try {
+ cameraService.addListener(mServiceListener);
+ mCameraService = cameraService;
+ } catch(CameraRuntimeException e) {
+ // Unexpected failure
+ throw new IllegalStateException("Failed to register a camera service listener",
+ e.asChecked());
+ } catch (RemoteException e) {
+ // Camera service is now down, leave mCameraService as null
+ }
+ }
+
+ /**
+ * Return a best-effort ICameraService.
+ *
+ * <p>This will be null if the camera service
+ * is not currently available. If the camera service has died since the last
+ * use of the camera service, will try to reconnect to the service.</p>
+ */
+ private ICameraService getCameraServiceLocked() {
+ if (mCameraService == null) {
+ Log.i(TAG, "getCameraServiceLocked: Reconnecting to camera service");
+ connectCameraServiceLocked();
+ if (mCameraService == null) {
+ Log.e(TAG, "Camera service is unavailable");
+ }
+ }
+ return mCameraService;
+ }
+
+ /**
+ * Listener for camera service death.
+ *
+ * <p>The camera service isn't supposed to die under any normal circumstances, but can be turned
+ * off during debug, or crash due to bugs. So detect that and null out the interface object, so
+ * that the next calls to the manager can try to reconnect.</p>
+ */
+ private class CameraServiceDeathListener implements IBinder.DeathRecipient {
+ public void binderDied() {
+ synchronized(mLock) {
+ mCameraService = null;
+ // Tell listeners that the cameras are _available_, because any existing clients
+ // will have gotten disconnected. This is optimistic under the assumption that the
+ // service will be back shortly.
+ //
+ // Without this, a camera service crash while a camera is open will never signal to
+ // listeners that previously in-use cameras are now available.
+ for (String cameraId : mDeviceIdList) {
+ mServiceListener.onStatusChangedLocked(CameraServiceListener.STATUS_PRESENT,
+ cameraId);
+ }
+ }
}
}
@@ -595,77 +707,102 @@ public final class CameraManager {
}
}
+ private void postSingleUpdate(final AvailabilityListener listener, final Handler handler,
+ final String id, final int status) {
+ if (isAvailable(status)) {
+ handler.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ listener.onCameraAvailable(id);
+ }
+ });
+ } else {
+ handler.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ listener.onCameraUnavailable(id);
+ }
+ });
+ }
+ }
+
+ /**
+ * Send the state of all known cameras to the provided listener, to initialize
+ * the listener's knowledge of camera state.
+ */
+ public void updateListenerLocked(AvailabilityListener listener, Handler handler) {
+ for (int i = 0; i < mDeviceStatus.size(); i++) {
+ String id = mDeviceStatus.keyAt(i);
+ Integer status = mDeviceStatus.valueAt(i);
+ postSingleUpdate(listener, handler, id, status);
+ }
+ }
+
@Override
public void onStatusChanged(int status, int cameraId) throws RemoteException {
synchronized(CameraManager.this.mLock) {
+ onStatusChangedLocked(status, String.valueOf(cameraId));
+ }
+ }
+ public void onStatusChangedLocked(int status, String id) {
+ if (DEBUG) {
Log.v(TAG,
- String.format("Camera id %d has status changed to 0x%x", cameraId, status));
-
- final String id = String.valueOf(cameraId);
+ String.format("Camera id %s has status changed to 0x%x", id, status));
+ }
- if (!validStatus(status)) {
- Log.e(TAG, String.format("Ignoring invalid device %d status 0x%x", cameraId,
- status));
- return;
- }
+ if (!validStatus(status)) {
+ Log.e(TAG, String.format("Ignoring invalid device %s status 0x%x", id,
+ status));
+ return;
+ }
- Integer oldStatus = mDeviceStatus.put(id, status);
+ Integer oldStatus = mDeviceStatus.put(id, status);
- if (oldStatus != null && oldStatus == status) {
+ if (oldStatus != null && oldStatus == status) {
+ if (DEBUG) {
Log.v(TAG, String.format(
- "Device status changed to 0x%x, which is what it already was",
- status));
- return;
+ "Device status changed to 0x%x, which is what it already was",
+ status));
}
+ return;
+ }
- // TODO: consider abstracting out this state minimization + transition
- // into a separate
- // more easily testable class
- // i.e. (new State()).addState(STATE_AVAILABLE)
- // .addState(STATE_NOT_AVAILABLE)
- // .addTransition(STATUS_PRESENT, STATE_AVAILABLE),
- // .addTransition(STATUS_NOT_PRESENT, STATE_NOT_AVAILABLE)
- // .addTransition(STATUS_ENUMERATING, STATE_NOT_AVAILABLE);
- // .addTransition(STATUS_NOT_AVAILABLE, STATE_NOT_AVAILABLE);
-
- // Translate all the statuses to either 'available' or 'not available'
- // available -> available => no new update
- // not available -> not available => no new update
- if (oldStatus != null && isAvailable(status) == isAvailable(oldStatus)) {
-
+ // TODO: consider abstracting out this state minimization + transition
+ // into a separate
+ // more easily testable class
+ // i.e. (new State()).addState(STATE_AVAILABLE)
+ // .addState(STATE_NOT_AVAILABLE)
+ // .addTransition(STATUS_PRESENT, STATE_AVAILABLE),
+ // .addTransition(STATUS_NOT_PRESENT, STATE_NOT_AVAILABLE)
+ // .addTransition(STATUS_ENUMERATING, STATE_NOT_AVAILABLE);
+ // .addTransition(STATUS_NOT_AVAILABLE, STATE_NOT_AVAILABLE);
+
+ // Translate all the statuses to either 'available' or 'not available'
+ // available -> available => no new update
+ // not available -> not available => no new update
+ if (oldStatus != null && isAvailable(status) == isAvailable(oldStatus)) {
+ if (DEBUG) {
Log.v(TAG,
String.format(
- "Device status was previously available (%d), " +
- " and is now again available (%d)" +
- "so no new client visible update will be sent",
- isAvailable(status), isAvailable(status)));
- return;
+ "Device status was previously available (%d), " +
+ " and is now again available (%d)" +
+ "so no new client visible update will be sent",
+ isAvailable(status), isAvailable(status)));
}
+ return;
+ }
+
+ final int listenerCount = mListenerMap.size();
+ for (int i = 0; i < listenerCount; i++) {
+ Handler handler = mListenerMap.valueAt(i);
+ final AvailabilityListener listener = mListenerMap.keyAt(i);
+
+ postSingleUpdate(listener, handler, id, status);
+ }
+ } // onStatusChangedLocked
- final int listenerCount = mListenerMap.size();
- for (int i = 0; i < listenerCount; i++) {
- Handler handler = mListenerMap.valueAt(i);
- final AvailabilityListener listener = mListenerMap.keyAt(i);
- if (isAvailable(status)) {
- handler.post(
- new Runnable() {
- @Override
- public void run() {
- listener.onCameraAvailable(id);
- }
- });
- } else {
- handler.post(
- new Runnable() {
- @Override
- public void run() {
- listener.onCameraUnavailable(id);
- }
- });
- }
- } // for
- } // synchronized
- } // onStatusChanged
} // CameraServiceListener
} // CameraManager
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index fb1bc15..ed4e457 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -294,8 +294,6 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice {
final int code = failureCode;
final boolean isError = failureIsError;
synchronized(mInterfaceLock) {
- if (mRemoteDevice == null) return; // Camera already closed, can't go to error state
-
mInError = true;
mDeviceHandler.post(new Runnable() {
@Override
diff --git a/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java b/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java
index 898c746..83ebadd 100644
--- a/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java
+++ b/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java
@@ -28,7 +28,7 @@ import android.os.RemoteException;
import java.lang.reflect.Method;
/**
- * Translate camera service status_t return values into exceptions.
+ * Translate camera device status_t return values into exceptions.
*
* @see android.hardware.camera2.utils.CameraBinderDecorator#newInstance
* @hide
@@ -57,7 +57,7 @@ public class CameraBinderDecorator {
public static final int EUSERS = -87;
- private static class CameraBinderDecoratorListener implements Decorator.DecoratorListener {
+ static class CameraBinderDecoratorListener implements Decorator.DecoratorListener {
@Override
public void onBeforeInvocation(Method m, Object[] args) {
@@ -76,10 +76,9 @@ public class CameraBinderDecorator {
public boolean onCatchException(Method m, Object[] args, Throwable t) {
if (t instanceof DeadObjectException) {
- UncheckedThrow.throwAnyException(new CameraRuntimeException(
- CAMERA_DISCONNECTED,
+ throw new CameraRuntimeException(CAMERA_DISCONNECTED,
"Process hosting the camera service has died unexpectedly",
- t));
+ t);
} else if (t instanceof RemoteException) {
throw new UnsupportedOperationException("An unknown RemoteException was thrown" +
" which should never happen.", t);
@@ -112,26 +111,20 @@ public class CameraBinderDecorator {
case BAD_VALUE:
throw new IllegalArgumentException("Bad argument passed to camera service");
case DEAD_OBJECT:
- UncheckedThrow.throwAnyException(new CameraRuntimeException(
- CAMERA_DISCONNECTED));
+ throw new CameraRuntimeException(CAMERA_DISCONNECTED);
case EACCES:
- UncheckedThrow.throwAnyException(new CameraRuntimeException(
- CAMERA_DISABLED));
+ throw new CameraRuntimeException(CAMERA_DISABLED);
case EBUSY:
- UncheckedThrow.throwAnyException(new CameraRuntimeException(
- CAMERA_IN_USE));
+ throw new CameraRuntimeException(CAMERA_IN_USE);
case EUSERS:
- UncheckedThrow.throwAnyException(new CameraRuntimeException(
- MAX_CAMERAS_IN_USE));
+ throw new CameraRuntimeException(MAX_CAMERAS_IN_USE);
case ENODEV:
- UncheckedThrow.throwAnyException(new CameraRuntimeException(
- CAMERA_DISCONNECTED));
+ throw new CameraRuntimeException(CAMERA_DISCONNECTED);
case EOPNOTSUPP:
- UncheckedThrow.throwAnyException(new CameraRuntimeException(
- CAMERA_DEPRECATED_HAL));
+ throw new CameraRuntimeException(CAMERA_DEPRECATED_HAL);
case INVALID_OPERATION:
- UncheckedThrow.throwAnyException(new IllegalStateException(
- "Illegal state encountered in camera service."));
+ throw new IllegalStateException(
+ "Illegal state encountered in camera service.");
}
/**
diff --git a/core/java/android/hardware/camera2/utils/CameraServiceBinderDecorator.java b/core/java/android/hardware/camera2/utils/CameraServiceBinderDecorator.java
new file mode 100644
index 0000000..c1fb6b1
--- /dev/null
+++ b/core/java/android/hardware/camera2/utils/CameraServiceBinderDecorator.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.utils;
+
+import android.os.DeadObjectException;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.lang.reflect.Method;
+
+/**
+ * Translate camera service status_t return values into exceptions.
+ *
+ * @see android.hardware.camera2.utils.CameraBinderDecorator#newInstance
+ * @hide
+ */
+public class CameraServiceBinderDecorator extends CameraBinderDecorator {
+
+ private static final String TAG = "CameraServiceBinderDecorator";
+
+ static class CameraServiceBinderDecoratorListener
+ extends CameraBinderDecorator.CameraBinderDecoratorListener {
+
+ // Pass through remote exceptions, unlike CameraBinderDecorator
+ @Override
+ public boolean onCatchException(Method m, Object[] args, Throwable t) {
+
+ if (t instanceof DeadObjectException) {
+ // Can sometimes happen (camera service died)
+ // Pass on silently
+ } else if (t instanceof RemoteException) {
+ // Some other kind of remote exception - this is not normal, so let's at least
+ // note it before moving on
+ Log.e(TAG, "Unexpected RemoteException from camera service call.", t);
+ }
+ // All other exceptions also get sent onward
+ return false;
+ }
+
+ }
+
+ /**
+ * <p>
+ * Wraps the type T with a proxy that will check 'status_t' return codes
+ * from the native side of the camera service, and throw Java exceptions
+ * automatically based on the code.
+ * </p>
+ *
+ * @param obj object that will serve as the target for all method calls
+ * @param <T> the type of the element you want to wrap. This must be an interface.
+ * @return a proxy that will intercept all invocations to obj
+ */
+ public static <T> T newInstance(T obj) {
+ return Decorator.<T> newInstance(obj, new CameraServiceBinderDecoratorListener());
+ }
+}