summaryrefslogtreecommitdiffstats
path: root/core/java/android/hardware
diff options
context:
space:
mode:
authorIgor Murashkin <iam@google.com>2013-06-25 20:26:06 +0000
committerIgor Murashkin <iam@google.com>2013-06-26 13:19:44 -0700
commite363fbb2647aeb5ef4c87160d84c6b9ae8d45598 (patch)
treeff832d9b46118be173d254ad269c93627ce9e6d1 /core/java/android/hardware
parent73d5fe9f2e6edc11327b4a211f7d077e1e52cbb2 (diff)
downloadframeworks_base-e363fbb2647aeb5ef4c87160d84c6b9ae8d45598.zip
frameworks_base-e363fbb2647aeb5ef4c87160d84c6b9ae8d45598.tar.gz
frameworks_base-e363fbb2647aeb5ef4c87160d84c6b9ae8d45598.tar.bz2
Partial CameraManager implementation
Bug: 9213377 Change-Id: I8f89fb94d7081a71b38e5cd0ad89116d219b4c33
Diffstat (limited to 'core/java/android/hardware')
-rw-r--r--core/java/android/hardware/photography/CameraAccessException.java30
-rw-r--r--core/java/android/hardware/photography/CameraDevice.java12
-rw-r--r--core/java/android/hardware/photography/CameraManager.java241
-rw-r--r--core/java/android/hardware/photography/utils/CameraBinderDecorator.java146
-rw-r--r--core/java/android/hardware/photography/utils/CameraRuntimeException.java63
-rw-r--r--core/java/android/hardware/photography/utils/Decorator.java92
-rw-r--r--core/java/android/hardware/photography/utils/UncheckedThrow.java40
7 files changed, 614 insertions, 10 deletions
diff --git a/core/java/android/hardware/photography/CameraAccessException.java b/core/java/android/hardware/photography/CameraAccessException.java
index 01114df..fac5086 100644
--- a/core/java/android/hardware/photography/CameraAccessException.java
+++ b/core/java/android/hardware/photography/CameraAccessException.java
@@ -16,6 +16,8 @@
package android.hardware.photography;
+import android.util.AndroidException;
+
/**
* <p><code>CameraAccessException</code> is thrown if a camera device could not
* be queried or opened by the {@link CameraManager}, or if the connection to an
@@ -24,7 +26,7 @@ package android.hardware.photography;
* @see CameraManager
* @see CameraDevice
*/
-public class CameraAccessException extends Exception {
+public class CameraAccessException extends AndroidException {
/**
* The camera device is in use already
*/
@@ -51,7 +53,10 @@ public class CameraAccessException extends Exception {
*/
public static final int CAMERA_DISCONNECTED = 4;
- private int mReason;
+ // Make the eclipse warning about serializable exceptions go away
+ private static final long serialVersionUID = 5630338637471475675L; // randomly generated
+
+ private final int mReason;
/**
* The reason for the failure to access the camera.
@@ -66,6 +71,7 @@ public class CameraAccessException extends Exception {
}
public CameraAccessException(int problem) {
+ super(getDefaultMessage(problem));
mReason = problem;
}
@@ -80,7 +86,25 @@ public class CameraAccessException extends Exception {
}
public CameraAccessException(int problem, Throwable cause) {
- super(cause);
+ super(getDefaultMessage(problem), cause);
mReason = problem;
}
+
+ private static String getDefaultMessage(int problem) {
+ switch (problem) {
+ case CAMERA_IN_USE:
+ return "The camera device is in use already";
+ case MAX_CAMERAS_IN_USE:
+ return "The system-wide limit for number of open cameras has been reached, " +
+ "and more camera devices cannot be opened until previous instances " +
+ "are closed.";
+ case CAMERA_DISABLED:
+ return "The camera is disabled due to a device policy, and cannot be opened.";
+ case CAMERA_DISCONNECTED:
+ return "The camera device is removable and has been disconnected from the Android" +
+ " device, or the camera service has shut down the connection due to a " +
+ "higher-priority access request for the camera device.";
+ }
+ return null;
+ }
}
diff --git a/core/java/android/hardware/photography/CameraDevice.java b/core/java/android/hardware/photography/CameraDevice.java
index 2062db2..e94e3a1 100644
--- a/core/java/android/hardware/photography/CameraDevice.java
+++ b/core/java/android/hardware/photography/CameraDevice.java
@@ -17,8 +17,10 @@
package android.hardware.photography;
import android.graphics.ImageFormat;
+import android.os.IBinder;
import android.renderscript.Allocation;
import android.renderscript.RenderScript;
+import android.util.Log;
import android.view.Surface;
import java.lang.AutoCloseable;
@@ -101,6 +103,8 @@ public final class CameraDevice implements AutoCloseable {
*/
public static final int TEMPLATE_MANUAL = 5;
+ private static final String TAG = "CameraDevice";
+
/**
* Get the static properties for this camera. These are identical to the
* properties returned by {@link CameraManager#getCameraProperties}.
@@ -451,6 +455,7 @@ public final class CameraDevice implements AutoCloseable {
* the camera device interface will throw a {@link IllegalStateException},
* except for calls to close().
*/
+ @Override
public void close() {
}
@@ -552,4 +557,11 @@ public final class CameraDevice implements AutoCloseable {
public void onCameraDeviceError(CameraDevice camera, int error);
}
+ /**
+ * @hide
+ */
+ public CameraDevice(IBinder binder) {
+ Log.e(TAG, "CameraDevice constructor not implemented yet");
+ }
+
}
diff --git a/core/java/android/hardware/photography/CameraManager.java b/core/java/android/hardware/photography/CameraManager.java
index 328ba4b..115f151 100644
--- a/core/java/android/hardware/photography/CameraManager.java
+++ b/core/java/android/hardware/photography/CameraManager.java
@@ -16,6 +16,22 @@
package android.hardware.photography;
+import android.content.Context;
+import android.hardware.ICameraService;
+import android.hardware.ICameraServiceListener;
+import android.hardware.IProCameraUser;
+import android.hardware.photography.utils.CameraBinderDecorator;
+import android.hardware.photography.utils.CameraRuntimeException;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+
/**
* <p>An interface for iterating, listing, and connecting to
* {@link CameraDevice CameraDevices}.</p>
@@ -32,9 +48,40 @@ package android.hardware.photography;
public final class CameraManager {
/**
+ * This should match the ICameraService definition
+ */
+ private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
+ private static final int USE_CALLING_UID = -1;
+
+ private final ICameraService mCameraService;
+ private ArrayList<String> mDeviceIdList;
+ private HashSet<CameraListener> mListenerSet;
+ private final Context mContext;
+ private final Object mLock = new Object();
+
+ /**
* @hide
*/
- public CameraManager() {
+ 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 {
+ mCameraService.addListener(new CameraServiceListener());
+ } catch(CameraRuntimeException e) {
+ throw new IllegalStateException("Failed to register a camera service listener",
+ e.asChecked());
+ } catch (RemoteException e) {
+ // impossible
+ }
}
/**
@@ -45,25 +92,37 @@ public final class CameraManager {
*
* @return The list of currently connected camera devices.
*/
- public String[] getDeviceIdList() {
- return null;
+ public String[] getDeviceIdList() throws CameraAccessException {
+ synchronized (mLock) {
+ return (String[]) getOrCreateDeviceIdListLocked().toArray();
+ }
}
/**
* Register a listener to be notified about camera device availability.
*
- * @param listener the new listener to send camera availablity notices to.
+ * Registering a listener more than once has no effect.
+ *
+ * @param listener the new listener to send camera availability notices to.
*/
public void registerCameraListener(CameraListener listener) {
+ synchronized (mLock) {
+ mListenerSet.add(listener);
+ }
}
/**
* Remove a previously-added listener; the listener will no longer receive
* connection and disconnection callbacks.
*
+ * Removing a listener that isn't registered has no effect.
+ *
* @param listener the listener to remove from the notification list
*/
public void unregisterCameraListener(CameraListener listener) {
+ synchronized (mLock) {
+ mListenerSet.remove(listener);
+ }
}
/**
@@ -84,7 +143,18 @@ public final class CameraManager {
*/
public CameraProperties getCameraProperties(String cameraId)
throws CameraAccessException {
- throw new IllegalArgumentException();
+
+ synchronized (mLock) {
+ if (!getOrCreateDeviceIdListLocked().contains(cameraId)) {
+ throw new IllegalArgumentException(String.format("Camera id %s does not match any" +
+ " currently connected camera device", cameraId));
+ }
+ }
+
+ // TODO: implement and call a service function to get the capabilities on C++ side
+
+ // TODO: get properties from service
+ return new CameraProperties();
}
/**
@@ -107,7 +177,33 @@ public final class CameraManager {
* @see android.app.admin.DevicePolicyManager#setCameraDisabled
*/
public CameraDevice openCamera(String cameraId) throws CameraAccessException {
- throw new IllegalArgumentException();
+
+ try {
+ IProCameraUser cameraUser;
+
+ synchronized (mLock) {
+ // TODO: Use ICameraDevice or some such instead of this...
+ cameraUser = mCameraService.connectPro(null,
+ Integer.parseInt(cameraId),
+ mContext.getPackageName(), USE_CALLING_UID);
+
+ }
+
+ return new CameraDevice(cameraUser.asBinder());
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Expected cameraId to be numeric, but it was: "
+ + cameraId);
+ } catch (CameraRuntimeException e) {
+ if (e.getReason() == CameraAccessException.CAMERA_DISCONNECTED) {
+ throw new IllegalArgumentException("Invalid camera ID specified -- " +
+ "perhaps the camera was physically disconnected", e);
+ } else {
+ throw e.asChecked();
+ }
+ } catch (RemoteException e) {
+ // impossible
+ return null;
+ }
}
/**
@@ -135,4 +231,135 @@ public final class CameraManager {
*/
public void onCameraUnavailable(String cameraId);
}
-}
+
+ private ArrayList<String> getOrCreateDeviceIdListLocked() throws CameraAccessException {
+ if (mDeviceIdList == null) {
+ int numCameras = 0;
+
+ try {
+ numCameras = mCameraService.getNumberOfCameras();
+ } catch(CameraRuntimeException e) {
+ throw e.asChecked();
+ } catch (RemoteException e) {
+ // impossible
+ return null;
+ }
+
+ mDeviceIdList = new ArrayList<String>();
+ for (int i = 0; i < numCameras; ++i) {
+ // Non-removable cameras use integers starting at 0 for their
+ // identifiers
+ mDeviceIdList.add(String.valueOf(i));
+ }
+
+ }
+ return mDeviceIdList;
+ }
+
+ // TODO: this class needs unit tests
+ // TODO: extract class into top level
+ private class CameraServiceListener extends Binder implements ICameraServiceListener {
+
+ // Keep up-to-date with ICameraServiceListener.h
+
+ // Device physically unplugged
+ public static final int STATUS_NOT_PRESENT = 0;
+ // Device physically has been plugged in
+ // and the camera can be used exclusively
+ public static final int STATUS_PRESENT = 1;
+ // Device physically has been plugged in
+ // but it will not be connect-able until enumeration is complete
+ public static final int STATUS_ENUMERATING = 2;
+ // Camera is in use by another app and cannot be used exclusively
+ public static final int STATUS_NOT_AVAILABLE = 0x80000000;
+
+ // Camera ID -> Status map
+ private final HashMap<String, Integer> mDeviceStatus = new HashMap<String, Integer>();
+
+ private static final String TAG = "CameraServiceListener";
+
+ @Override
+ public IBinder asBinder() {
+ return this;
+ }
+
+ private boolean isAvailable(int status) {
+ switch (status) {
+ case STATUS_PRESENT:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private boolean validStatus(int status) {
+ switch (status) {
+ case STATUS_NOT_PRESENT:
+ case STATUS_PRESENT:
+ case STATUS_ENUMERATING:
+ case STATUS_NOT_AVAILABLE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public void onStatusChanged(int status, int cameraId) throws RemoteException {
+ synchronized(CameraManager.this) {
+
+ Log.v(TAG,
+ String.format("Camera id %d has status changed to 0x%x", cameraId, status));
+
+ String id = String.valueOf(cameraId);
+
+ if (!validStatus(status)) {
+ Log.e(TAG, String.format("Ignoring invalid device %d status 0x%x", cameraId,
+ status));
+ return;
+ }
+
+ Integer oldStatus = mDeviceStatus.put(id, status);
+
+ if (oldStatus == status) {
+ Log.v(TAG, String.format(
+ "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)) {
+
+ 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;
+ }
+
+ for (CameraListener listener : mListenerSet) {
+ if (isAvailable(status)) {
+ listener.onCameraAvailable(id);
+ } else {
+ listener.onCameraUnavailable(id);
+ }
+ } // for
+ } // synchronized
+ } // onStatusChanged
+ } // CameraServiceListener
+} // CameraManager
diff --git a/core/java/android/hardware/photography/utils/CameraBinderDecorator.java b/core/java/android/hardware/photography/utils/CameraBinderDecorator.java
new file mode 100644
index 0000000..99e7c78
--- /dev/null
+++ b/core/java/android/hardware/photography/utils/CameraBinderDecorator.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2013 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.photography.utils;
+
+import static android.hardware.photography.CameraAccessException.CAMERA_DISABLED;
+import static android.hardware.photography.CameraAccessException.CAMERA_DISCONNECTED;
+import static android.hardware.photography.CameraAccessException.CAMERA_IN_USE;
+
+import android.os.DeadObjectException;
+import android.os.RemoteException;
+
+import java.lang.reflect.Method;
+
+/**
+ * Translate camera service status_t return values into exceptions.
+ *
+ * @see android.hardware.photography.utils.CameraBinderDecorator#newInstance
+ * @hide
+ */
+public class CameraBinderDecorator {
+
+ public static final int NO_ERROR = 0;
+ public static final int PERMISSION_DENIED = -1;
+ public static final int ALREADY_EXISTS = -17;
+ public static final int BAD_VALUE = -22;
+ public static final int DEAD_OBJECT = -32;
+
+ /**
+ * TODO: add as error codes in Errors.h
+ * - POLICY_PROHIBITS
+ * - RESOURCE_BUSY
+ * - NO_SUCH_DEVICE
+ */
+ public static final int EACCES = -13;
+ public static final int EBUSY = -16;
+ public static final int ENODEV = -19;
+
+ private static class CameraBinderDecoratorListener implements Decorator.DecoratorListener {
+
+ @Override
+ public void onBeforeInvocation(Method m, Object[] args) {
+ }
+
+ @Override
+ public void onAfterInvocation(Method m, Object[] args, Object result) {
+ // int return type => status_t => convert to exception
+ if (m.getReturnType() == Integer.TYPE) {
+ int returnValue = (Integer) result;
+
+ switch (returnValue) {
+ case NO_ERROR:
+ return;
+ case PERMISSION_DENIED:
+ throw new SecurityException("Lacking privileges to access camera service");
+ case ALREADY_EXISTS:
+ return;
+ case BAD_VALUE:
+ throw new IllegalArgumentException("Bad argument passed to camera service");
+ case DEAD_OBJECT:
+ UncheckedThrow.throwAnyException(new CameraRuntimeException(
+ CAMERA_DISCONNECTED));
+ // TODO: Camera service (native side) should return
+ // EACCES error
+ // when there's a policy manager disabled causing this
+ case EACCES:
+ UncheckedThrow.throwAnyException(new CameraRuntimeException(
+ CAMERA_DISABLED));
+ case EBUSY:
+ UncheckedThrow.throwAnyException(new CameraRuntimeException(
+ CAMERA_IN_USE));
+ case ENODEV:
+ UncheckedThrow.throwAnyException(new CameraRuntimeException(
+ CAMERA_DISCONNECTED));
+ }
+
+ /**
+ * Trap the rest of the negative return values. If we have known
+ * error codes i.e. ALREADY_EXISTS that aren't really runtime
+ * errors, then add them to the top switch statement
+ */
+ if (returnValue < 0) {
+ throw new UnsupportedOperationException(String.format("Unknown error %d",
+ returnValue));
+ }
+ }
+ }
+
+ @Override
+ public boolean onCatchException(Method m, Object[] args, Throwable t) {
+
+ if (t instanceof DeadObjectException) {
+ UncheckedThrow.throwAnyException(new CameraRuntimeException(
+ CAMERA_DISCONNECTED,
+ "Process hosting the camera service has died unexpectedly",
+ t));
+ } else if (t instanceof RemoteException) {
+ throw new UnsupportedOperationException("An unknown RemoteException was thrown" +
+ " which should never happen.", t);
+ }
+
+ return false;
+ }
+
+ @Override
+ public void onFinally(Method m, Object[] args) {
+ }
+
+ }
+
+ /**
+ * <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>
+ * <p>
+ * In addition it also rewrites binder's RemoteException into either a
+ * CameraAccessException or an UnsupportedOperationException.
+ * </p>
+ * <p>
+ * As a result of calling any method on the proxy, RemoteException is
+ * guaranteed never to be thrown.
+ * </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 CameraBinderDecoratorListener());
+ }
+}
diff --git a/core/java/android/hardware/photography/utils/CameraRuntimeException.java b/core/java/android/hardware/photography/utils/CameraRuntimeException.java
new file mode 100644
index 0000000..25dfc62
--- /dev/null
+++ b/core/java/android/hardware/photography/utils/CameraRuntimeException.java
@@ -0,0 +1,63 @@
+package android.hardware.photography.utils;
+
+import android.hardware.photography.CameraAccessException;
+
+/**
+ * @hide
+ */
+public class CameraRuntimeException extends RuntimeException {
+
+ private final int mReason;
+ private String mMessage;
+ private Throwable mCause;
+
+ public final int getReason() {
+ return mReason;
+ }
+
+ public CameraRuntimeException(int problem) {
+ super();
+ mReason = problem;
+ }
+
+ public CameraRuntimeException(int problem, String message) {
+ super(message);
+ mReason = problem;
+ mMessage = message;
+ }
+
+ public CameraRuntimeException(int problem, String message, Throwable cause) {
+ super(message, cause);
+ mReason = problem;
+ mMessage = message;
+ mCause = cause;
+ }
+
+ public CameraRuntimeException(int problem, Throwable cause) {
+ super(cause);
+ mReason = problem;
+ mCause = cause;
+ }
+
+ /**
+ * Recreate this exception as the CameraAccessException equivalent.
+ * @return CameraAccessException
+ */
+ public CameraAccessException asChecked() {
+ CameraAccessException e;
+
+ if (mMessage != null && mCause != null) {
+ e = new CameraAccessException(mReason, mMessage, mCause);
+ } else if (mMessage != null) {
+ e = new CameraAccessException(mReason, mMessage);
+ } else if (mCause != null) {
+ e = new CameraAccessException(mReason, mCause);
+ } else {
+ e = new CameraAccessException(mReason);
+ }
+ // throw and catch, so java has a chance to fill out the stack trace
+ e.setStackTrace(this.getStackTrace());
+
+ return e;
+ }
+}
diff --git a/core/java/android/hardware/photography/utils/Decorator.java b/core/java/android/hardware/photography/utils/Decorator.java
new file mode 100644
index 0000000..ed05dd2
--- /dev/null
+++ b/core/java/android/hardware/photography/utils/Decorator.java
@@ -0,0 +1,92 @@
+
+package android.hardware.photography.utils;
+
+import java.lang.reflect.*;
+
+/**
+ * This is an implementation of the 'decorator' design pattern using Java's proxy mechanism.
+ *
+ * @see android.hardware.photography.utils.Decorator#newInstance
+ *
+ * @hide
+ */
+public class Decorator<T> implements InvocationHandler {
+
+ public interface DecoratorListener {
+ /**
+ * This method is called before the target method is invoked
+ * @param args arguments to target method
+ * @param m Method being called
+ */
+ void onBeforeInvocation(Method m, Object[] args);
+ /**
+ * This function is called after the target method is invoked
+ * if there were no uncaught exceptions
+ * @param args arguments to target method
+ * @param m Method being called
+ * @param result return value of target method
+ */
+ void onAfterInvocation(Method m, Object[] args, Object result);
+ /**
+ * This method is called only if there was an exception thrown by the target method
+ * during its invocation.
+ *
+ * @param args arguments to target method
+ * @param m Method being called
+ * @param t Throwable that was thrown
+ * @return false to rethrow exception, true if the exception was handled
+ */
+ boolean onCatchException(Method m, Object[] args, Throwable t);
+ /**
+ * This is called after the target method is invoked, regardless of whether or not
+ * there were any exceptions.
+ * @param args arguments to target method
+ * @param m Method being called
+ */
+ void onFinally(Method m, Object[] args);
+ }
+
+ private final T mObject;
+ private final DecoratorListener mListener;
+
+ /**
+ * Create a decorator wrapping the specified object's method calls.
+ *
+ * @param obj the object whose method calls you want to intercept
+ * @param listener the decorator handler for intercepted method calls
+ * @param <T> the type of the element you want to wrap. This must be an interface.
+ * @return a wrapped interface-compatible T
+ */
+ @SuppressWarnings("unchecked")
+ public static<T> T newInstance(T obj, DecoratorListener listener) {
+ return (T)java.lang.reflect.Proxy.newProxyInstance(
+ obj.getClass().getClassLoader(),
+ obj.getClass().getInterfaces(),
+ new Decorator<T>(obj, listener));
+ }
+
+ private Decorator(T obj, DecoratorListener listener) {
+ this.mObject = obj;
+ this.mListener = listener;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method m, Object[] args)
+ throws Throwable
+ {
+ Object result = null;
+ try {
+ mListener.onBeforeInvocation(m, args);
+ result = m.invoke(mObject, args);
+ mListener.onAfterInvocation(m, args, result);
+ } catch (InvocationTargetException e) {
+ Throwable t = e.getTargetException();
+ if (!mListener.onCatchException(m, args, t)) {
+ throw t;
+ }
+ } finally {
+ mListener.onFinally(m, args);
+ }
+ return result;
+ }
+}
diff --git a/core/java/android/hardware/photography/utils/UncheckedThrow.java b/core/java/android/hardware/photography/utils/UncheckedThrow.java
new file mode 100644
index 0000000..8eed6b1
--- /dev/null
+++ b/core/java/android/hardware/photography/utils/UncheckedThrow.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2013 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.photography.utils;
+
+/**
+ * @hide
+ */
+public class UncheckedThrow {
+
+ /**
+ * Throw any kind of exception without needing it to be checked
+ * @param e any instance of a Exception
+ */
+ public static void throwAnyException(Exception e) {
+ /**
+ * Abuse type erasure by making the compiler think we are throwing RuntimeException,
+ * which is unchecked, but then inserting any exception in there.
+ */
+ UncheckedThrow.<RuntimeException>throwAnyImpl(e);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static<T extends Exception> void throwAnyImpl(Exception e) throws T {
+ throw (T) e;
+ }
+}