diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index a9a72b074df86..7095e4d498ab6 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -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).
* - *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.
+ *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}.
* - *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.
+ *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.
* *Using larger resolution outputs, or more outputs, can result in slower * output rate from the device.
* - *Configuring the outputs with an empty or null list will transition - * the camera into an {@link StateListener#onUnconfigured unconfigured state}. - *
+ *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}.
* *Calling configureOutputs with the same arguments as the last call to - * configureOutputs has no effect.
+ * configureOutputs has no effect, and the {@link StateListener#onBusy busy} + * and {@link StateListener#onIdle idle} state transitions will happen + * immediately. * * @param outputs The new set of Surfaces that should be made available as * targets for captured image data. @@ -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 */ @@ -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. - * - *The events reported here are device-wide; notifications about - * individual capture requests or capture results are reported through - * {@link CaptureListener}.
- * - *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. - *
- * - * @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. @@ -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. + * + *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.
+ * + *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.
+ * + *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.
*/ @Override public void close(); - // TODO: We should decide on the behavior of in-flight requests should be on close. /** *A listener for tracking the progress of a {@link CaptureRequest} @@ -713,6 +709,9 @@ public interface CameraDevice extends AutoCloseable { * A listener for notifications about the state of a camera * device. * + *
A listener must be provided to the {@link CameraManager#openCamera} + * method to open a camera device.
+ * *These events include notifications about the device becoming idle ( * allowing for {@link #configureOutputs} to be called), about device * disconnection, and about unexpected device errors.
@@ -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 { /** diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index f5ee367c70ca0..65b6c7a094431 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -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 @@ -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) { @@ -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(), @@ -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) { @@ -238,7 +245,6 @@ public final class CameraManager { throw e.asChecked(); } catch (RemoteException e) { // impossible - return null; } } @@ -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); } /** diff --git a/core/java/android/hardware/camera2/impl/CameraDevice.java b/core/java/android/hardware/camera2/impl/CameraDevice.java index 463063c1fb2ac..c5d0999213068 100644 --- a/core/java/android/hardware/camera2/impl/CameraDevice.java +++ b/core/java/android/hardware/camera2/impl/CameraDevice.java @@ -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