Loading core/java/android/hardware/camera2/CameraDevice.java +42 −43 Original line number Diff line number Diff line Loading @@ -197,26 +197,33 @@ public interface CameraDevice extends AutoCloseable { * if the format is user-visible, it must be one of android.scaler.availableFormats; * and the size must be one of android.scaler.available[Processed|Jpeg]Sizes).</p> * * <p>To change the output, the camera device must be idle. The device is considered * to be idle once all in-flight and pending capture requests have been processed, * and all output image buffers from the captures have been sent to their destination * Surfaces.</p> * * <p>To reach an idle state without cancelling any submitted captures, first * stop any repeating request/burst with {@link #stopRepeating}, and then * wait for the {@link StateListener#onIdle} callback to be * called. To idle as fast as possible, use {@link #flush} and wait for the * idle callback.</p> * <p>When this method is called with valid Surfaces, the device will transition to the {@link * StateListener#onBusy busy state}. Once configuration is complete, the device will transition * into the {@link StateListener#onIdle idle state}. Capture requests using the newly-configured * Surfaces may then be submitted with {@link #capture}, {@link #captureBurst}, {@link * #setRepeatingRequest}, or {@link #setRepeatingBurst}.</p> * * <p>If this method is called while the camera device is still actively processing previously * submitted captures, then the following sequence of events occurs: The device transitions to * the busy state and calls the {@link StateListener#onBusy} callback. Second, if a repeating * request is set it is cleared. Third, the device finishes up all in-flight and pending * requests. Finally, once the device is idle, it then reconfigures its outputs, and calls the * {@link StateListener#onIdle} method once it is again ready to accept capture * requests. Therefore, no submitted work is discarded. To idle as fast as possible, use {@link * #flush} and wait for the idle callback before calling configureOutputs. This will discard * work, but reaches the new configuration sooner.</p> * * <p>Using larger resolution outputs, or more outputs, can result in slower * output rate from the device.</p> * * <p>Configuring the outputs with an empty or null list will transition * the camera into an {@link StateListener#onUnconfigured unconfigured state}. * </p> * <p>Configuring the outputs with an empty or null list will transition the camera into an * {@link StateListener#onUnconfigured unconfigured state} instead of the {@link * StateListener#onIdle idle state}. </p> * * <p>Calling configureOutputs with the same arguments as the last call to * configureOutputs has no effect.</p> * configureOutputs has no effect, and the {@link StateListener#onBusy busy} * and {@link StateListener#onIdle idle} state transitions will happen * immediately.</p> * * @param outputs The new set of Surfaces that should be made available as * targets for captured image data. Loading @@ -228,7 +235,10 @@ public interface CameraDevice extends AutoCloseable { * @throws IllegalStateException if the camera device is not idle, or * if the camera device has been closed * * @see StateListener#onBusy * @see StateListener#onIdle * @see StateListener#onActive * @see StateListener#onUnconfigured * @see #stopRepeating * @see #flush */ Loading Loading @@ -515,31 +525,6 @@ public interface CameraDevice extends AutoCloseable { */ public void waitUntilIdle() throws CameraAccessException; /** * Set the listener object to call when an asynchronous device event occurs, * such as errors or idle notifications. * * <p>The events reported here are device-wide; notifications about * individual capture requests or capture results are reported through * {@link CaptureListener}.</p> * * <p>If the camera device is idle when the listener is set, then the * {@link StateListener#onIdle} method will be immediately called, * even if the device has never been active before. * </p> * * @param listener the CameraDeviceListener to send device-level event * notifications to. Setting this to null will stop notifications. * @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}. * * @throws IllegalArgumentException if handler is null, the listener is * not null, and the calling thread has no looper * * @hide */ public void setDeviceListener(StateListener listener, Handler handler); /** * Flush all captures currently pending and in-progress as fast as * possible. Loading Loading @@ -577,13 +562,24 @@ public interface CameraDevice extends AutoCloseable { public void flush() throws CameraAccessException; /** * Close the connection to this camera device. After this call, all calls to * Close the connection to this camera device. * * <p>After this call, all calls to * the camera device interface will throw a {@link IllegalStateException}, * except for calls to close(). * except for calls to close(). Once the device has fully shut down, the * {@link StateListener#onClosed} callback will be called, and the camera is * free to be re-opened.</p> * * <p>After this call, besides the final {@link StateListener#onClosed} call, no calls to the * device's {@link StateListener} will occur, and any remaining submitted capture requests will * not fire their {@link CaptureListener} callbacks.</p> * * <p>To shut down as fast as possible, call the {@link #flush} method and then {@link #close} * once the flush completes. This will discard some capture requests, but results in faster * shutdown.</p> */ @Override public void close(); // TODO: We should decide on the behavior of in-flight requests should be on close. /** * <p>A listener for tracking the progress of a {@link CaptureRequest} Loading Loading @@ -713,6 +709,9 @@ public interface CameraDevice extends AutoCloseable { * A listener for notifications about the state of a camera * device. * * <p>A listener must be provided to the {@link CameraManager#openCamera} * method to open a camera device.</p> * * <p>These events include notifications about the device becoming idle ( * allowing for {@link #configureOutputs} to be called), about device * disconnection, and about unexpected device errors.</p> Loading @@ -722,7 +721,7 @@ public interface CameraDevice extends AutoCloseable { * the {@link #capture}, {@link #captureBurst}, {@link * #setRepeatingRequest}, or {@link #setRepeatingBurst} methods. * * @see #setDeviceListener * @see CameraManager#openCamera */ public static abstract class StateListener { /** Loading core/java/android/hardware/camera2/CameraManager.java +13 −16 Original line number Diff line number Diff line Loading @@ -197,6 +197,8 @@ public final class CameraManager { * {@link #openCamera}. * * @param cameraId The unique identifier of the camera device to open * @param listener The listener for the camera. Must not be null. * @param handler The handler to call the listener on. Must not be null. * * @throws CameraAccessException if the camera is disabled by device policy, * or too many camera devices are already open, or the cameraId does not match Loading @@ -204,11 +206,14 @@ public final class CameraManager { * * @throws SecurityException if the application does not have permission to * access the camera * @throws IllegalArgumentException if listener or handler is null. * * @see #getCameraIdList * @see android.app.admin.DevicePolicyManager#setCameraDisabled */ private CameraDevice openCamera(String cameraId) throws CameraAccessException { private void openCameraDeviceUserAsync(String cameraId, CameraDevice.StateListener listener, Handler handler) throws CameraAccessException { try { synchronized (mLock) { Loading @@ -216,7 +221,10 @@ public final class CameraManager { ICameraDeviceUser cameraUser; android.hardware.camera2.impl.CameraDevice device = new android.hardware.camera2.impl.CameraDevice(cameraId); new android.hardware.camera2.impl.CameraDevice( cameraId, listener, handler); BinderHolder holder = new BinderHolder(); mCameraService.connectDevice(device.getCallbacks(), Loading @@ -225,10 +233,9 @@ public final class CameraManager { cameraUser = ICameraDeviceUser.Stub.asInterface(holder.getBinder()); // TODO: factor out listener to be non-nested, then move setter to constructor // For now, calling setRemoteDevice will fire initial // onOpened/onUnconfigured callbacks. device.setRemoteDevice(cameraUser); return device; } } catch (NumberFormatException e) { Loading @@ -238,7 +245,6 @@ public final class CameraManager { throw e.asChecked(); } catch (RemoteException e) { // impossible return null; } } Loading Loading @@ -303,16 +309,7 @@ public final class CameraManager { } } final CameraDevice camera = openCamera(cameraId); camera.setDeviceListener(listener, handler); // TODO: make truly async in the camera service handler.post(new Runnable() { @Override public void run() { listener.onOpened(camera); } }); openCameraDeviceUserAsync(cameraId, listener, handler); } /** Loading core/java/android/hardware/camera2/impl/CameraDevice.java +148 −55 Original line number Diff line number Diff line Loading @@ -55,8 +55,10 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { private final Object mLock = new Object(); private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks(); private StateListener mDeviceListener; private Handler mDeviceHandler; private final StateListener mDeviceListener; private final Handler mDeviceHandler; private boolean mIdle = true; private final SparseArray<CaptureListenerHolder> mCaptureListenerMap = new SparseArray<CaptureListenerHolder>(); Loading @@ -67,8 +69,72 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { private final String mCameraId; public CameraDevice(String cameraId) { // Runnables for all state transitions, except error, which needs the // error code argument private final Runnable mCallOnOpened = new Runnable() { public void run() { if (!CameraDevice.this.isClosed()) { mDeviceListener.onOpened(CameraDevice.this); } } }; private final Runnable mCallOnUnconfigured = new Runnable() { public void run() { if (!CameraDevice.this.isClosed()) { mDeviceListener.onUnconfigured(CameraDevice.this); } } }; private final Runnable mCallOnActive = new Runnable() { public void run() { if (!CameraDevice.this.isClosed()) { mDeviceListener.onActive(CameraDevice.this); } } }; private final Runnable mCallOnBusy = new Runnable() { public void run() { if (!CameraDevice.this.isClosed()) { mDeviceListener.onBusy(CameraDevice.this); } } }; private final Runnable mCallOnClosed = new Runnable() { public void run() { if (!CameraDevice.this.isClosed()) { mDeviceListener.onClosed(CameraDevice.this); } } }; private final Runnable mCallOnIdle = new Runnable() { public void run() { if (!CameraDevice.this.isClosed()) { mDeviceListener.onIdle(CameraDevice.this); } } }; private final Runnable mCallOnDisconnected = new Runnable() { public void run() { if (!CameraDevice.this.isClosed()) { mDeviceListener.onDisconnected(CameraDevice.this); } } }; public CameraDevice(String cameraId, StateListener listener, Handler handler) { if (cameraId == null || listener == null || handler == null) { throw new IllegalArgumentException("Null argument given"); } mCameraId = cameraId; mDeviceListener = listener; mDeviceHandler = handler; TAG = String.format("CameraDevice-%s-JV", mCameraId); DEBUG = Log.isLoggable(TAG, Log.DEBUG); } Loading @@ -79,7 +145,12 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { public void setRemoteDevice(ICameraDeviceUser remoteDevice) { // TODO: Move from decorator to direct binder-mediated exceptions synchronized(mLock) { mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice); mDeviceHandler.post(mCallOnOpened); mDeviceHandler.post(mCallOnUnconfigured); } } @Override Loading @@ -89,7 +160,13 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { @Override public void configureOutputs(List<Surface> outputs) throws CameraAccessException { // Treat a null input the same an empty list if (outputs == null) { outputs = new ArrayList<Surface>(); } synchronized (mLock) { checkIfCameraClosed(); HashSet<Surface> addSet = new HashSet<Surface>(outputs); // Streams to create List<Integer> deleteList = new ArrayList<Integer>(); // Streams to delete Loading @@ -105,9 +182,13 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { } } mDeviceHandler.post(mCallOnBusy); stopRepeating(); try { // TODO: mRemoteDevice.beginConfigure mRemoteDevice.waitUntilIdle(); // TODO: mRemoteDevice.beginConfigure // Delete all streams first (to free up HW resources) for (Integer streamId : deleteList) { mRemoteDevice.deleteStream(streamId); Loading @@ -126,7 +207,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { } catch (CameraRuntimeException e) { if (e.getReason() == CAMERA_IN_USE) { throw new IllegalStateException("The camera is currently busy." + " You must call waitUntilIdle before trying to reconfigure."); " You must wait until the previous operation completes."); } throw e.asChecked(); Loading @@ -134,6 +215,12 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { // impossible return; } if (outputs.size() > 0) { mDeviceHandler.post(mCallOnIdle); } else { mDeviceHandler.post(mCallOnUnconfigured); } } } Loading @@ -141,6 +228,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { public CaptureRequest.Builder createCaptureRequest(int templateType) throws CameraAccessException { synchronized (mLock) { checkIfCameraClosed(); CameraMetadataNative templatedRequest = new CameraMetadataNative(); Loading Loading @@ -188,7 +276,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { } synchronized (mLock) { checkIfCameraClosed(); int requestId; try { Loading @@ -208,6 +296,11 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { mRepeatingRequestIdStack.add(requestId); } if (mIdle) { mDeviceHandler.post(mCallOnActive); } mIdle = false; return requestId; } } Loading @@ -233,7 +326,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { public void stopRepeating() throws CameraAccessException { synchronized (mLock) { checkIfCameraClosed(); while (!mRepeatingRequestIdStack.isEmpty()) { int requestId = mRepeatingRequestIdStack.pop(); Loading Loading @@ -269,21 +362,12 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { } } @Override public void setDeviceListener(StateListener listener, Handler handler) { synchronized (mLock) { if (listener != null) { handler = checkHandler(handler); } mDeviceListener = listener; mDeviceHandler = handler; } } @Override public void flush() throws CameraAccessException { synchronized (mLock) { checkIfCameraClosed(); mDeviceHandler.post(mCallOnBusy); try { mRemoteDevice.flush(); } catch (CameraRuntimeException e) { Loading @@ -297,9 +381,6 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { @Override public void close() { // TODO: every method should throw IllegalStateException after close has been called synchronized (mLock) { try { Loading @@ -312,8 +393,11 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { // impossible } mRemoteDevice = null; if (mRemoteDevice != null) { mDeviceHandler.post(mCallOnClosed); } mRemoteDevice = null; } } Loading Loading @@ -399,49 +483,44 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { @Override public void onCameraError(final int errorCode) { synchronized (mLock) { if (CameraDevice.this.mDeviceListener == null) return; final StateListener listener = CameraDevice.this.mDeviceListener; Runnable r = null; if (isClosed()) return; synchronized(mLock) { switch (errorCode) { case ERROR_CAMERA_DISCONNECTED: r = new Runnable() { public void run() { listener.onDisconnected(CameraDevice.this); } }; r = 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() { public void run() { listener.onError(CameraDevice.this, errorCode); if (!CameraDevice.this.isClosed()) { mDeviceListener.onError(CameraDevice.this, errorCode); } } }; break; default: Log.e(TAG, "Unknown error from camera device: " + errorCode); } if (r != null) { CameraDevice.this.mDeviceHandler.post(r); } } } @Override public void onCameraIdle() { if (isClosed()) return; if (DEBUG) { Log.d(TAG, "Camera now idle"); } synchronized (mLock) { if (CameraDevice.this.mDeviceListener == null) return; final StateListener listener = CameraDevice.this.mDeviceListener; Runnable r = new Runnable() { public void run() { listener.onIdle(CameraDevice.this); if (!CameraDevice.this.mIdle) { CameraDevice.this.mDeviceHandler.post(mCallOnIdle); } }; CameraDevice.this.mDeviceHandler.post(r); CameraDevice.this.mIdle = true; } } Loading @@ -461,15 +540,19 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { return; } if (isClosed()) return; // Dispatch capture start notice holder.getHandler().post( new Runnable() { public void run() { if (!CameraDevice.this.isClosed()) { holder.getListener().onCaptureStarted( CameraDevice.this, holder.getRequest(), timestamp); } } }); } Loading Loading @@ -503,6 +586,8 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { return; } if (isClosed()) return; final CaptureRequest request = holder.getRequest(); final CaptureResult resultAsCapture = new CaptureResult(result, request, requestId); Loading @@ -510,11 +595,13 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { new Runnable() { @Override public void run() { if (!CameraDevice.this.isClosed()){ holder.getListener().onCaptureCompleted( CameraDevice.this, request, resultAsCapture); } } }); } Loading @@ -541,4 +628,10 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { throw new IllegalStateException("CameraDevice was already closed"); } } private boolean isClosed() { synchronized(mLock) { return (mRemoteDevice == null); } } } Loading
core/java/android/hardware/camera2/CameraDevice.java +42 −43 Original line number Diff line number Diff line Loading @@ -197,26 +197,33 @@ public interface CameraDevice extends AutoCloseable { * if the format is user-visible, it must be one of android.scaler.availableFormats; * and the size must be one of android.scaler.available[Processed|Jpeg]Sizes).</p> * * <p>To change the output, the camera device must be idle. The device is considered * to be idle once all in-flight and pending capture requests have been processed, * and all output image buffers from the captures have been sent to their destination * Surfaces.</p> * * <p>To reach an idle state without cancelling any submitted captures, first * stop any repeating request/burst with {@link #stopRepeating}, and then * wait for the {@link StateListener#onIdle} callback to be * called. To idle as fast as possible, use {@link #flush} and wait for the * idle callback.</p> * <p>When this method is called with valid Surfaces, the device will transition to the {@link * StateListener#onBusy busy state}. Once configuration is complete, the device will transition * into the {@link StateListener#onIdle idle state}. Capture requests using the newly-configured * Surfaces may then be submitted with {@link #capture}, {@link #captureBurst}, {@link * #setRepeatingRequest}, or {@link #setRepeatingBurst}.</p> * * <p>If this method is called while the camera device is still actively processing previously * submitted captures, then the following sequence of events occurs: The device transitions to * the busy state and calls the {@link StateListener#onBusy} callback. Second, if a repeating * request is set it is cleared. Third, the device finishes up all in-flight and pending * requests. Finally, once the device is idle, it then reconfigures its outputs, and calls the * {@link StateListener#onIdle} method once it is again ready to accept capture * requests. Therefore, no submitted work is discarded. To idle as fast as possible, use {@link * #flush} and wait for the idle callback before calling configureOutputs. This will discard * work, but reaches the new configuration sooner.</p> * * <p>Using larger resolution outputs, or more outputs, can result in slower * output rate from the device.</p> * * <p>Configuring the outputs with an empty or null list will transition * the camera into an {@link StateListener#onUnconfigured unconfigured state}. * </p> * <p>Configuring the outputs with an empty or null list will transition the camera into an * {@link StateListener#onUnconfigured unconfigured state} instead of the {@link * StateListener#onIdle idle state}. </p> * * <p>Calling configureOutputs with the same arguments as the last call to * configureOutputs has no effect.</p> * configureOutputs has no effect, and the {@link StateListener#onBusy busy} * and {@link StateListener#onIdle idle} state transitions will happen * immediately.</p> * * @param outputs The new set of Surfaces that should be made available as * targets for captured image data. Loading @@ -228,7 +235,10 @@ public interface CameraDevice extends AutoCloseable { * @throws IllegalStateException if the camera device is not idle, or * if the camera device has been closed * * @see StateListener#onBusy * @see StateListener#onIdle * @see StateListener#onActive * @see StateListener#onUnconfigured * @see #stopRepeating * @see #flush */ Loading Loading @@ -515,31 +525,6 @@ public interface CameraDevice extends AutoCloseable { */ public void waitUntilIdle() throws CameraAccessException; /** * Set the listener object to call when an asynchronous device event occurs, * such as errors or idle notifications. * * <p>The events reported here are device-wide; notifications about * individual capture requests or capture results are reported through * {@link CaptureListener}.</p> * * <p>If the camera device is idle when the listener is set, then the * {@link StateListener#onIdle} method will be immediately called, * even if the device has never been active before. * </p> * * @param listener the CameraDeviceListener to send device-level event * notifications to. Setting this to null will stop notifications. * @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}. * * @throws IllegalArgumentException if handler is null, the listener is * not null, and the calling thread has no looper * * @hide */ public void setDeviceListener(StateListener listener, Handler handler); /** * Flush all captures currently pending and in-progress as fast as * possible. Loading Loading @@ -577,13 +562,24 @@ public interface CameraDevice extends AutoCloseable { public void flush() throws CameraAccessException; /** * Close the connection to this camera device. After this call, all calls to * Close the connection to this camera device. * * <p>After this call, all calls to * the camera device interface will throw a {@link IllegalStateException}, * except for calls to close(). * except for calls to close(). Once the device has fully shut down, the * {@link StateListener#onClosed} callback will be called, and the camera is * free to be re-opened.</p> * * <p>After this call, besides the final {@link StateListener#onClosed} call, no calls to the * device's {@link StateListener} will occur, and any remaining submitted capture requests will * not fire their {@link CaptureListener} callbacks.</p> * * <p>To shut down as fast as possible, call the {@link #flush} method and then {@link #close} * once the flush completes. This will discard some capture requests, but results in faster * shutdown.</p> */ @Override public void close(); // TODO: We should decide on the behavior of in-flight requests should be on close. /** * <p>A listener for tracking the progress of a {@link CaptureRequest} Loading Loading @@ -713,6 +709,9 @@ public interface CameraDevice extends AutoCloseable { * A listener for notifications about the state of a camera * device. * * <p>A listener must be provided to the {@link CameraManager#openCamera} * method to open a camera device.</p> * * <p>These events include notifications about the device becoming idle ( * allowing for {@link #configureOutputs} to be called), about device * disconnection, and about unexpected device errors.</p> Loading @@ -722,7 +721,7 @@ public interface CameraDevice extends AutoCloseable { * the {@link #capture}, {@link #captureBurst}, {@link * #setRepeatingRequest}, or {@link #setRepeatingBurst} methods. * * @see #setDeviceListener * @see CameraManager#openCamera */ public static abstract class StateListener { /** Loading
core/java/android/hardware/camera2/CameraManager.java +13 −16 Original line number Diff line number Diff line Loading @@ -197,6 +197,8 @@ public final class CameraManager { * {@link #openCamera}. * * @param cameraId The unique identifier of the camera device to open * @param listener The listener for the camera. Must not be null. * @param handler The handler to call the listener on. Must not be null. * * @throws CameraAccessException if the camera is disabled by device policy, * or too many camera devices are already open, or the cameraId does not match Loading @@ -204,11 +206,14 @@ public final class CameraManager { * * @throws SecurityException if the application does not have permission to * access the camera * @throws IllegalArgumentException if listener or handler is null. * * @see #getCameraIdList * @see android.app.admin.DevicePolicyManager#setCameraDisabled */ private CameraDevice openCamera(String cameraId) throws CameraAccessException { private void openCameraDeviceUserAsync(String cameraId, CameraDevice.StateListener listener, Handler handler) throws CameraAccessException { try { synchronized (mLock) { Loading @@ -216,7 +221,10 @@ public final class CameraManager { ICameraDeviceUser cameraUser; android.hardware.camera2.impl.CameraDevice device = new android.hardware.camera2.impl.CameraDevice(cameraId); new android.hardware.camera2.impl.CameraDevice( cameraId, listener, handler); BinderHolder holder = new BinderHolder(); mCameraService.connectDevice(device.getCallbacks(), Loading @@ -225,10 +233,9 @@ public final class CameraManager { cameraUser = ICameraDeviceUser.Stub.asInterface(holder.getBinder()); // TODO: factor out listener to be non-nested, then move setter to constructor // For now, calling setRemoteDevice will fire initial // onOpened/onUnconfigured callbacks. device.setRemoteDevice(cameraUser); return device; } } catch (NumberFormatException e) { Loading @@ -238,7 +245,6 @@ public final class CameraManager { throw e.asChecked(); } catch (RemoteException e) { // impossible return null; } } Loading Loading @@ -303,16 +309,7 @@ public final class CameraManager { } } final CameraDevice camera = openCamera(cameraId); camera.setDeviceListener(listener, handler); // TODO: make truly async in the camera service handler.post(new Runnable() { @Override public void run() { listener.onOpened(camera); } }); openCameraDeviceUserAsync(cameraId, listener, handler); } /** Loading
core/java/android/hardware/camera2/impl/CameraDevice.java +148 −55 Original line number Diff line number Diff line Loading @@ -55,8 +55,10 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { private final Object mLock = new Object(); private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks(); private StateListener mDeviceListener; private Handler mDeviceHandler; private final StateListener mDeviceListener; private final Handler mDeviceHandler; private boolean mIdle = true; private final SparseArray<CaptureListenerHolder> mCaptureListenerMap = new SparseArray<CaptureListenerHolder>(); Loading @@ -67,8 +69,72 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { private final String mCameraId; public CameraDevice(String cameraId) { // Runnables for all state transitions, except error, which needs the // error code argument private final Runnable mCallOnOpened = new Runnable() { public void run() { if (!CameraDevice.this.isClosed()) { mDeviceListener.onOpened(CameraDevice.this); } } }; private final Runnable mCallOnUnconfigured = new Runnable() { public void run() { if (!CameraDevice.this.isClosed()) { mDeviceListener.onUnconfigured(CameraDevice.this); } } }; private final Runnable mCallOnActive = new Runnable() { public void run() { if (!CameraDevice.this.isClosed()) { mDeviceListener.onActive(CameraDevice.this); } } }; private final Runnable mCallOnBusy = new Runnable() { public void run() { if (!CameraDevice.this.isClosed()) { mDeviceListener.onBusy(CameraDevice.this); } } }; private final Runnable mCallOnClosed = new Runnable() { public void run() { if (!CameraDevice.this.isClosed()) { mDeviceListener.onClosed(CameraDevice.this); } } }; private final Runnable mCallOnIdle = new Runnable() { public void run() { if (!CameraDevice.this.isClosed()) { mDeviceListener.onIdle(CameraDevice.this); } } }; private final Runnable mCallOnDisconnected = new Runnable() { public void run() { if (!CameraDevice.this.isClosed()) { mDeviceListener.onDisconnected(CameraDevice.this); } } }; public CameraDevice(String cameraId, StateListener listener, Handler handler) { if (cameraId == null || listener == null || handler == null) { throw new IllegalArgumentException("Null argument given"); } mCameraId = cameraId; mDeviceListener = listener; mDeviceHandler = handler; TAG = String.format("CameraDevice-%s-JV", mCameraId); DEBUG = Log.isLoggable(TAG, Log.DEBUG); } Loading @@ -79,7 +145,12 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { public void setRemoteDevice(ICameraDeviceUser remoteDevice) { // TODO: Move from decorator to direct binder-mediated exceptions synchronized(mLock) { mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice); mDeviceHandler.post(mCallOnOpened); mDeviceHandler.post(mCallOnUnconfigured); } } @Override Loading @@ -89,7 +160,13 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { @Override public void configureOutputs(List<Surface> outputs) throws CameraAccessException { // Treat a null input the same an empty list if (outputs == null) { outputs = new ArrayList<Surface>(); } synchronized (mLock) { checkIfCameraClosed(); HashSet<Surface> addSet = new HashSet<Surface>(outputs); // Streams to create List<Integer> deleteList = new ArrayList<Integer>(); // Streams to delete Loading @@ -105,9 +182,13 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { } } mDeviceHandler.post(mCallOnBusy); stopRepeating(); try { // TODO: mRemoteDevice.beginConfigure mRemoteDevice.waitUntilIdle(); // TODO: mRemoteDevice.beginConfigure // Delete all streams first (to free up HW resources) for (Integer streamId : deleteList) { mRemoteDevice.deleteStream(streamId); Loading @@ -126,7 +207,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { } catch (CameraRuntimeException e) { if (e.getReason() == CAMERA_IN_USE) { throw new IllegalStateException("The camera is currently busy." + " You must call waitUntilIdle before trying to reconfigure."); " You must wait until the previous operation completes."); } throw e.asChecked(); Loading @@ -134,6 +215,12 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { // impossible return; } if (outputs.size() > 0) { mDeviceHandler.post(mCallOnIdle); } else { mDeviceHandler.post(mCallOnUnconfigured); } } } Loading @@ -141,6 +228,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { public CaptureRequest.Builder createCaptureRequest(int templateType) throws CameraAccessException { synchronized (mLock) { checkIfCameraClosed(); CameraMetadataNative templatedRequest = new CameraMetadataNative(); Loading Loading @@ -188,7 +276,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { } synchronized (mLock) { checkIfCameraClosed(); int requestId; try { Loading @@ -208,6 +296,11 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { mRepeatingRequestIdStack.add(requestId); } if (mIdle) { mDeviceHandler.post(mCallOnActive); } mIdle = false; return requestId; } } Loading @@ -233,7 +326,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { public void stopRepeating() throws CameraAccessException { synchronized (mLock) { checkIfCameraClosed(); while (!mRepeatingRequestIdStack.isEmpty()) { int requestId = mRepeatingRequestIdStack.pop(); Loading Loading @@ -269,21 +362,12 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { } } @Override public void setDeviceListener(StateListener listener, Handler handler) { synchronized (mLock) { if (listener != null) { handler = checkHandler(handler); } mDeviceListener = listener; mDeviceHandler = handler; } } @Override public void flush() throws CameraAccessException { synchronized (mLock) { checkIfCameraClosed(); mDeviceHandler.post(mCallOnBusy); try { mRemoteDevice.flush(); } catch (CameraRuntimeException e) { Loading @@ -297,9 +381,6 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { @Override public void close() { // TODO: every method should throw IllegalStateException after close has been called synchronized (mLock) { try { Loading @@ -312,8 +393,11 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { // impossible } mRemoteDevice = null; if (mRemoteDevice != null) { mDeviceHandler.post(mCallOnClosed); } mRemoteDevice = null; } } Loading Loading @@ -399,49 +483,44 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { @Override public void onCameraError(final int errorCode) { synchronized (mLock) { if (CameraDevice.this.mDeviceListener == null) return; final StateListener listener = CameraDevice.this.mDeviceListener; Runnable r = null; if (isClosed()) return; synchronized(mLock) { switch (errorCode) { case ERROR_CAMERA_DISCONNECTED: r = new Runnable() { public void run() { listener.onDisconnected(CameraDevice.this); } }; r = 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() { public void run() { listener.onError(CameraDevice.this, errorCode); if (!CameraDevice.this.isClosed()) { mDeviceListener.onError(CameraDevice.this, errorCode); } } }; break; default: Log.e(TAG, "Unknown error from camera device: " + errorCode); } if (r != null) { CameraDevice.this.mDeviceHandler.post(r); } } } @Override public void onCameraIdle() { if (isClosed()) return; if (DEBUG) { Log.d(TAG, "Camera now idle"); } synchronized (mLock) { if (CameraDevice.this.mDeviceListener == null) return; final StateListener listener = CameraDevice.this.mDeviceListener; Runnable r = new Runnable() { public void run() { listener.onIdle(CameraDevice.this); if (!CameraDevice.this.mIdle) { CameraDevice.this.mDeviceHandler.post(mCallOnIdle); } }; CameraDevice.this.mDeviceHandler.post(r); CameraDevice.this.mIdle = true; } } Loading @@ -461,15 +540,19 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { return; } if (isClosed()) return; // Dispatch capture start notice holder.getHandler().post( new Runnable() { public void run() { if (!CameraDevice.this.isClosed()) { holder.getListener().onCaptureStarted( CameraDevice.this, holder.getRequest(), timestamp); } } }); } Loading Loading @@ -503,6 +586,8 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { return; } if (isClosed()) return; final CaptureRequest request = holder.getRequest(); final CaptureResult resultAsCapture = new CaptureResult(result, request, requestId); Loading @@ -510,11 +595,13 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { new Runnable() { @Override public void run() { if (!CameraDevice.this.isClosed()){ holder.getListener().onCaptureCompleted( CameraDevice.this, request, resultAsCapture); } } }); } Loading @@ -541,4 +628,10 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { throw new IllegalStateException("CameraDevice was already closed"); } } private boolean isClosed() { synchronized(mLock) { return (mRemoteDevice == null); } } }