From 9129aa25c1e9e20a652aa09d5742e0c4789cffc4 Mon Sep 17 00:00:00 2001 From: Emilian Peev Date: Tue, 20 Mar 2018 15:39:34 +0000 Subject: [PATCH 1/2] Camera: Async camera manager calls should use executors Methods in camera manager that receive and invoke user supplied callbacks should also be able to accept executors and invoke them instead of using user handlers. Bug: 73953366 Test: Camera CTS Change-Id: I2f22ada463d7debb90beba3e31579cdb4df42506 --- api/current.txt | 3 + .../hardware/camera2/CameraManager.java | 102 +++++++++++++++--- .../camera2/impl/CameraDeviceImpl.java | 6 +- 3 files changed, 95 insertions(+), 16 deletions(-) diff --git a/api/current.txt b/api/current.txt index 9b492bcf422de..d602dc4693932 100644 --- a/api/current.txt +++ b/api/current.txt @@ -15901,8 +15901,11 @@ package android.hardware.camera2 { method public android.hardware.camera2.CameraCharacteristics getCameraCharacteristics(java.lang.String) throws android.hardware.camera2.CameraAccessException; method public java.lang.String[] getCameraIdList() throws android.hardware.camera2.CameraAccessException; method public void openCamera(java.lang.String, android.hardware.camera2.CameraDevice.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException; + method public void openCamera(java.lang.String, java.util.concurrent.Executor, android.hardware.camera2.CameraDevice.StateCallback) throws android.hardware.camera2.CameraAccessException; method public void registerAvailabilityCallback(android.hardware.camera2.CameraManager.AvailabilityCallback, android.os.Handler); + method public void registerAvailabilityCallback(java.util.concurrent.Executor, android.hardware.camera2.CameraManager.AvailabilityCallback); method public void registerTorchCallback(android.hardware.camera2.CameraManager.TorchCallback, android.os.Handler); + method public void registerTorchCallback(java.util.concurrent.Executor, android.hardware.camera2.CameraManager.TorchCallback); method public void setTorchMode(java.lang.String, boolean) throws android.hardware.camera2.CameraAccessException; method public void unregisterAvailabilityCallback(android.hardware.camera2.CameraManager.AvailabilityCallback); method public void unregisterTorchCallback(android.hardware.camera2.CameraManager.TorchCallback); diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index 4d642956b1350..4124536d6adc7 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -16,6 +16,7 @@ package android.hardware.camera2; +import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -133,6 +134,27 @@ public final class CameraManager { CameraDeviceImpl.checkAndWrapHandler(handler)); } + /** + * Register a callback to be notified about camera device availability. + * + *

The behavior of this method matches that of + * {@link #registerAvailabilityCallback(AvailabilityCallback, Handler)}, + * except that it uses {@link java.util.concurrent.Executor} as an argument + * instead of {@link android.os.Handler}.

+ * + * @param executor The executor which will be used to invoke the callback. + * @param callback the new callback to send camera availability notices to + * + * @throws IllegalArgumentException if the executor is {@code null}. + */ + public void registerAvailabilityCallback(@NonNull @CallbackExecutor Executor executor, + @NonNull AvailabilityCallback callback) { + if (executor == null) { + throw new IllegalArgumentException("executor was null"); + } + CameraManagerGlobal.get().registerAvailabilityCallback(callback, executor); + } + /** * Remove a previously-added callback; the callback will no longer receive connection and * disconnection callbacks. @@ -172,6 +194,27 @@ public final class CameraManager { CameraDeviceImpl.checkAndWrapHandler(handler)); } + /** + * Register a callback to be notified about torch mode status. + * + *

The behavior of this method matches that of + * {@link #registerTorchCallback(TorchCallback, Handler)}, + * except that it uses {@link java.util.concurrent.Executor} as an argument + * instead of {@link android.os.Handler}.

+ * + * @param executor The executor which will be used to invoke the callback + * @param callback The new callback to send torch mode status to + * + * @throws IllegalArgumentException if the executor is {@code null}. + */ + public void registerTorchCallback(@NonNull @CallbackExecutor Executor executor, + @NonNull TorchCallback callback) { + if (executor == null) { + throw new IllegalArgumentException("executor was null"); + } + CameraManagerGlobal.get().registerTorchCallback(callback, executor); + } + /** * Remove a previously-added callback; the callback will no longer receive torch mode status * callbacks. @@ -248,7 +291,7 @@ public final class CameraManager { * * @param cameraId The unique identifier of the camera device to open * @param callback The callback for the camera. Must not be null. - * @param handler The handler to invoke the callback on. Must not be null. + * @param executor The executor to invoke the callback with. Must not be null. * @param uid The UID of the application actually opening the camera. * Must be USE_CALLING_UID unless the caller is a service * that is trusted to open the device on behalf of an @@ -267,7 +310,7 @@ public final class CameraManager { * @see android.app.admin.DevicePolicyManager#setCameraDisabled */ private CameraDevice openCameraDeviceUserAsync(String cameraId, - CameraDevice.StateCallback callback, Handler handler, final int uid) + CameraDevice.StateCallback callback, Executor executor, final int uid) throws CameraAccessException { CameraCharacteristics characteristics = getCameraCharacteristics(cameraId); CameraDevice device = null; @@ -280,7 +323,7 @@ public final class CameraManager { new android.hardware.camera2.impl.CameraDeviceImpl( cameraId, callback, - handler, + executor, characteristics, mContext.getApplicationInfo().targetSdkVersion); @@ -421,7 +464,47 @@ public final class CameraManager { @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler) throws CameraAccessException { - openCameraForUid(cameraId, callback, handler, USE_CALLING_UID); + openCameraForUid(cameraId, callback, CameraDeviceImpl.checkAndWrapHandler(handler), + USE_CALLING_UID); + } + + /** + * Open a connection to a camera with the given ID. + * + *

The behavior of this method matches that of + * {@link #openCamera(String, StateCallback, Handler)}, except that it uses + * {@link java.util.concurrent.Executor} as an argument instead of + * {@link android.os.Handler}.

+ * + * @param cameraId + * The unique identifier of the camera device to open + * @param executor + * The executor which will be used when invoking the callback. + * @param callback + * The callback which is invoked once the camera is opened + * + * @throws CameraAccessException if the camera is disabled by device policy, + * has been disconnected, or is being used by a higher-priority camera API client. + * + * @throws IllegalArgumentException if cameraId, the callback or the executor was null, + * or the cameraId does not match any currently or previously available + * camera device. + * + * @throws SecurityException if the application does not have permission to + * access the camera + * + * @see #getCameraIdList + * @see android.app.admin.DevicePolicyManager#setCameraDisabled + */ + @RequiresPermission(android.Manifest.permission.CAMERA) + public void openCamera(@NonNull String cameraId, + @NonNull @CallbackExecutor Executor executor, + @NonNull final CameraDevice.StateCallback callback) + throws CameraAccessException { + if (executor == null) { + throw new IllegalArgumentException("executor was null"); + } + openCameraForUid(cameraId, callback, executor, USE_CALLING_UID); } /** @@ -440,7 +523,7 @@ public final class CameraManager { * @hide */ public void openCameraForUid(@NonNull String cameraId, - @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler, + @NonNull final CameraDevice.StateCallback callback, @NonNull Executor executor, int clientUid) throws CameraAccessException { @@ -448,19 +531,12 @@ public final class CameraManager { throw new IllegalArgumentException("cameraId was null"); } else if (callback == null) { throw new IllegalArgumentException("callback was null"); - } else if (handler == null) { - if (Looper.myLooper() != null) { - handler = new Handler(); - } else { - throw new IllegalArgumentException( - "Handler argument is null, but no looper exists in the calling thread"); - } } if (CameraManagerGlobal.sCameraServiceDisabled) { throw new IllegalArgumentException("No cameras available on device"); } - openCameraDeviceUserAsync(cameraId, callback, handler, clientUid); + openCameraDeviceUserAsync(cameraId, callback, executor, clientUid); } /** diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index 1f35f31b68ff3..46d4cbdeef62a 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -240,14 +240,14 @@ public class CameraDeviceImpl extends CameraDevice } }; - public CameraDeviceImpl(String cameraId, StateCallback callback, Handler handler, + public CameraDeviceImpl(String cameraId, StateCallback callback, Executor executor, CameraCharacteristics characteristics, int appTargetSdkVersion) { - if (cameraId == null || callback == null || handler == null || characteristics == null) { + if (cameraId == null || callback == null || executor == null || characteristics == null) { throw new IllegalArgumentException("Null argument given"); } mCameraId = cameraId; mDeviceCallback = callback; - mDeviceExecutor = checkAndWrapHandler(handler); + mDeviceExecutor = executor; mCharacteristics = characteristics; mAppTargetSdkVersion = appTargetSdkVersion; From 2f0184fa894780e3b3b81be6361b135bdfb0170f Mon Sep 17 00:00:00 2001 From: Emilian Peev Date: Wed, 21 Mar 2018 11:57:33 +0000 Subject: [PATCH 2/2] Camera: Accept user-supplied executors in capture sessions It should be possible for clients to pass executors instead of handlers in any method that accepts capture result listener. Bug: 3767382 Test: Camera CTS Change-Id: I605c001533e93e6294d265fcd4d3d5783c313b8a --- api/current.txt | 4 + .../camera2/CameraCaptureSession.java | 174 ++++++++++++++- .../impl/CameraCaptureSessionImpl.java | 209 ++++++++++++++---- ...onstrainedHighSpeedCaptureSessionImpl.java | 36 +++ .../camera2/impl/CameraDeviceImpl.java | 2 +- 5 files changed, 381 insertions(+), 44 deletions(-) diff --git a/api/current.txt b/api/current.txt index d602dc4693932..5030fa64224c5 100644 --- a/api/current.txt +++ b/api/current.txt @@ -15727,6 +15727,8 @@ package android.hardware.camera2 { method public abstract void abortCaptures() throws android.hardware.camera2.CameraAccessException; method public abstract int capture(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException; method public abstract int captureBurst(java.util.List, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException; + method public int captureBurstRequests(java.util.List, java.util.concurrent.Executor, android.hardware.camera2.CameraCaptureSession.CaptureCallback) throws android.hardware.camera2.CameraAccessException; + method public int captureSingleRequest(android.hardware.camera2.CaptureRequest, java.util.concurrent.Executor, android.hardware.camera2.CameraCaptureSession.CaptureCallback) throws android.hardware.camera2.CameraAccessException; method public abstract void close(); method public abstract void finalizeOutputConfigurations(java.util.List) throws android.hardware.camera2.CameraAccessException; method public abstract android.hardware.camera2.CameraDevice getDevice(); @@ -15734,7 +15736,9 @@ package android.hardware.camera2 { method public abstract boolean isReprocessable(); method public abstract void prepare(android.view.Surface) throws android.hardware.camera2.CameraAccessException; method public abstract int setRepeatingBurst(java.util.List, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException; + method public int setRepeatingBurstRequests(java.util.List, java.util.concurrent.Executor, android.hardware.camera2.CameraCaptureSession.CaptureCallback) throws android.hardware.camera2.CameraAccessException; method public abstract int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException; + method public int setSingleRepeatingRequest(android.hardware.camera2.CaptureRequest, java.util.concurrent.Executor, android.hardware.camera2.CameraCaptureSession.CaptureCallback) throws android.hardware.camera2.CameraAccessException; method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException; method public void updateOutputConfiguration(android.hardware.camera2.params.OutputConfiguration) throws android.hardware.camera2.CameraAccessException; } diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java index ff69bd89564cd..eafe5938d38bc 100644 --- a/core/java/android/hardware/camera2/CameraCaptureSession.java +++ b/core/java/android/hardware/camera2/CameraCaptureSession.java @@ -16,15 +16,16 @@ package android.hardware.camera2; +import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.hardware.camera2.params.OutputConfiguration; import android.os.Handler; import android.view.Surface; +import java.util.concurrent.Executor; import java.util.List; - /** * A configured capture session for a {@link CameraDevice}, used for capturing images from the * camera or reprocessing images captured from the camera in the same session previously. @@ -353,6 +354,50 @@ public abstract class CameraCaptureSession implements AutoCloseable { @Nullable CaptureCallback listener, @Nullable Handler handler) throws CameraAccessException; + /** + *

Submit a request for an image to be captured by the camera device.

+ * + *

The behavior of this method matches that of + * {@link #capture(CaptureRequest, CaptureCallback, Handler)}, + * except that it uses {@link java.util.concurrent.Executor} as an argument + * instead of {@link android.os.Handler}.

+ * + * @param request the settings for this capture + * @param executor the executor which will be used for invoking the listener. + * @param listener The callback object to notify once this request has been + * processed. + * + * @return int A unique capture sequence ID used by + * {@link CaptureCallback#onCaptureSequenceCompleted}. + * + * @throws CameraAccessException if the camera device is no longer connected or has + * encountered a fatal error + * @throws IllegalStateException if this session is no longer active, either because the session + * was explicitly closed, a new session has been created + * or the camera device has been closed. + * @throws IllegalArgumentException if the request targets no Surfaces or Surfaces that are not + * configured as outputs for this session; or the request + * targets a set of Surfaces that cannot be submitted + * simultaneously in a reprocessable capture session; or a + * reprocess capture request is submitted in a + * non-reprocessable capture session; or the reprocess capture + * request was created with a {@link TotalCaptureResult} from + * a different session; or the capture targets a Surface in + * the middle of being {@link #prepare prepared}; or the + * executor is null, or the listener is not null. + * + * @see #captureBurst + * @see #setRepeatingRequest + * @see #setRepeatingBurst + * @see #abortCaptures + * @see CameraDevice#createReprocessableCaptureSession + */ + public int captureSingleRequest(@NonNull CaptureRequest request, + @NonNull @CallbackExecutor Executor executor, @NonNull CaptureCallback listener) + throws CameraAccessException { + throw new UnsupportedOperationException("Subclasses must override this method"); + } + /** * Submit a list of requests to be captured in sequence as a burst. The * burst will be captured in the minimum amount of time possible, and will @@ -415,6 +460,53 @@ public abstract class CameraCaptureSession implements AutoCloseable { @Nullable CaptureCallback listener, @Nullable Handler handler) throws CameraAccessException; + /** + * Submit a list of requests to be captured in sequence as a burst. The + * burst will be captured in the minimum amount of time possible, and will + * not be interleaved with requests submitted by other capture or repeat + * calls. + * + *

The behavior of this method matches that of + * {@link #captureBurst(List, CaptureCallback, Handler)}, + * except that it uses {@link java.util.concurrent.Executor} as an argument + * instead of {@link android.os.Handler}.

+ * + * @param requests the list of settings for this burst capture + * @param executor the executor which will be used for invoking the listener. + * @param listener The callback object to notify each time one of the + * requests in the burst has been processed. + * + * @return int A unique capture sequence ID used by + * {@link CaptureCallback#onCaptureSequenceCompleted}. + * + * @throws CameraAccessException if the camera device is no longer connected or has + * encountered a fatal error + * @throws IllegalStateException if this session is no longer active, either because the session + * was explicitly closed, a new session has been created + * or the camera device has been closed. + * @throws IllegalArgumentException If the requests target no Surfaces, or the requests target + * Surfaces not currently configured as outputs; or one of the + * requests targets a set of Surfaces that cannot be submitted + * simultaneously in a reprocessable capture session; or a + * reprocess capture request is submitted in a + * non-reprocessable capture session; or one of the reprocess + * capture requests was created with a + * {@link TotalCaptureResult} from a different session; or one + * of the captures targets a Surface in the middle of being + * {@link #prepare prepared}; or if the executor is null; or if + * the listener is null. + * + * @see #capture + * @see #setRepeatingRequest + * @see #setRepeatingBurst + * @see #abortCaptures + */ + public int captureBurstRequests(@NonNull List requests, + @NonNull @CallbackExecutor Executor executor, @NonNull CaptureCallback listener) + throws CameraAccessException { + throw new UnsupportedOperationException("Subclasses must override this method"); + } + /** * Request endlessly repeating capture of images by this capture session. * @@ -482,6 +574,45 @@ public abstract class CameraCaptureSession implements AutoCloseable { @Nullable CaptureCallback listener, @Nullable Handler handler) throws CameraAccessException; + /** + * Request endlessly repeating capture of images by this capture session. + * + *

The behavior of this method matches that of + * {@link #setRepeatingRequest(CaptureRequest, CaptureCallback, Handler)}, + * except that it uses {@link java.util.concurrent.Executor} as an argument + * instead of {@link android.os.Handler}.

+ * + * @param request the request to repeat indefinitely + * @param executor the executor which will be used for invoking the listener. + * @param listener The callback object to notify every time the + * request finishes processing. + * + * @return int A unique capture sequence ID used by + * {@link CaptureCallback#onCaptureSequenceCompleted}. + * + * @throws CameraAccessException if the camera device is no longer connected or has + * encountered a fatal error + * @throws IllegalStateException if this session is no longer active, either because the session + * was explicitly closed, a new session has been created + * or the camera device has been closed. + * @throws IllegalArgumentException If the request references no Surfaces or references Surfaces + * that are not currently configured as outputs; or the request + * is a reprocess capture request; or the capture targets a + * Surface in the middle of being {@link #prepare prepared}; or + * the executor is null; or the listener is null. + * + * @see #capture + * @see #captureBurst + * @see #setRepeatingBurst + * @see #stopRepeating + * @see #abortCaptures + */ + public int setSingleRepeatingRequest(@NonNull CaptureRequest request, + @NonNull @CallbackExecutor Executor executor, @NonNull CaptureCallback listener) + throws CameraAccessException { + throw new UnsupportedOperationException("Subclasses must override this method"); + } + /** *

Request endlessly repeating capture of a sequence of images by this * capture session.

@@ -554,6 +685,47 @@ public abstract class CameraCaptureSession implements AutoCloseable { @Nullable CaptureCallback listener, @Nullable Handler handler) throws CameraAccessException; + /** + *

Request endlessly repeating capture of a sequence of images by this + * capture session.

+ * + *

The behavior of this method matches that of + * {@link #setRepeatingBurst(List, CaptureCallback, Handler)}, + * except that it uses {@link java.util.concurrent.Executor} as an argument + * instead of {@link android.os.Handler}.

+ * + * @param requests the list of requests to cycle through indefinitely + * @param executor the executor which will be used for invoking the listener. + * @param listener The callback object to notify each time one of the + * requests in the repeating bursts has finished processing. + * + * @return int A unique capture sequence ID used by + * {@link CaptureCallback#onCaptureSequenceCompleted}. + * + * @throws CameraAccessException if the camera device is no longer connected or has + * encountered a fatal error + * @throws IllegalStateException if this session is no longer active, either because the session + * was explicitly closed, a new session has been created + * or the camera device has been closed. + * @throws IllegalArgumentException If the requests reference no Surfaces or reference Surfaces + * not currently configured as outputs; or one of the requests + * is a reprocess capture request; or one of the captures + * targets a Surface in the middle of being + * {@link #prepare prepared}; or the executor is null; or the + * listener is null. + * + * @see #capture + * @see #captureBurst + * @see #setRepeatingRequest + * @see #stopRepeating + * @see #abortCaptures + */ + public int setRepeatingBurstRequests(@NonNull List requests, + @NonNull @CallbackExecutor Executor executor, @NonNull CaptureCallback listener) + throws CameraAccessException { + throw new UnsupportedOperationException("Subclasses must override this method"); + } + /** *

Cancel any ongoing repeating capture set by either * {@link #setRepeatingRequest setRepeatingRequest} or diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java index 9cac71c356453..a4640c1fa5197 100644 --- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java @@ -158,14 +158,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession @Override public int capture(CaptureRequest request, CaptureCallback callback, Handler handler) throws CameraAccessException { - if (request == null) { - throw new IllegalArgumentException("request must not be null"); - } else if (request.isReprocess() && !isReprocessable()) { - throw new IllegalArgumentException("this capture session cannot handle reprocess " + - "requests"); - } else if (request.isReprocess() && request.getReprocessableSessionId() != mId) { - throw new IllegalArgumentException("capture request was created for another session"); - } + checkCaptureRequest(request); synchronized (mDeviceImpl.mInterfaceLock) { checkNotClosed(); @@ -182,26 +175,46 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession } } + @Override + public int captureSingleRequest(CaptureRequest request, Executor executor, + CaptureCallback callback) throws CameraAccessException { + if (executor == null) { + throw new IllegalArgumentException("executor must not be null"); + } else if (callback == null) { + throw new IllegalArgumentException("callback must not be null"); + } + checkCaptureRequest(request); + + synchronized (mDeviceImpl.mInterfaceLock) { + checkNotClosed(); + + executor = CameraDeviceImpl.checkExecutor(executor, callback); + + if (DEBUG) { + Log.v(TAG, mIdString + "capture - request " + request + ", callback " + callback + + " executor " + executor); + } + + return addPendingSequence(mDeviceImpl.capture(request, + createCaptureCallbackProxyWithExecutor(executor, callback), mDeviceExecutor)); + } + } + + private void checkCaptureRequest(CaptureRequest request) { + if (request == null) { + throw new IllegalArgumentException("request must not be null"); + } else if (request.isReprocess() && !isReprocessable()) { + throw new IllegalArgumentException("this capture session cannot handle reprocess " + + "requests"); + } else if (request.isReprocess() && request.getReprocessableSessionId() != mId) { + throw new IllegalArgumentException("capture request was created for another session"); + } + } + @Override public int captureBurst(List requests, CaptureCallback callback, Handler handler) throws CameraAccessException { - if (requests == null) { - throw new IllegalArgumentException("Requests must not be null"); - } else if (requests.isEmpty()) { - throw new IllegalArgumentException("Requests must have at least one element"); - } - - for (CaptureRequest request : requests) { - if (request.isReprocess()) { - if (!isReprocessable()) { - throw new IllegalArgumentException("This capture session cannot handle " + - "reprocess requests"); - } else if (request.getReprocessableSessionId() != mId) { - throw new IllegalArgumentException("Capture request was created for another " + - "session"); - } - } - } + checkCaptureRequests(requests); synchronized (mDeviceImpl.mInterfaceLock) { checkNotClosed(); @@ -219,14 +232,57 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession } } + @Override + public int captureBurstRequests(List requests, Executor executor, + CaptureCallback callback) throws CameraAccessException { + if (executor == null) { + throw new IllegalArgumentException("executor must not be null"); + } else if (callback == null) { + throw new IllegalArgumentException("callback must not be null"); + } + checkCaptureRequests(requests); + + synchronized (mDeviceImpl.mInterfaceLock) { + checkNotClosed(); + + executor = CameraDeviceImpl.checkExecutor(executor, callback); + + if (DEBUG) { + CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]); + Log.v(TAG, mIdString + "captureBurst - requests " + Arrays.toString(requestArray) + + ", callback " + callback + " executor " + executor); + } + + return addPendingSequence(mDeviceImpl.captureBurst(requests, + createCaptureCallbackProxyWithExecutor(executor, callback), mDeviceExecutor)); + } + } + + private void checkCaptureRequests(List requests) { + if (requests == null) { + throw new IllegalArgumentException("Requests must not be null"); + } else if (requests.isEmpty()) { + throw new IllegalArgumentException("Requests must have at least one element"); + } + + for (CaptureRequest request : requests) { + if (request.isReprocess()) { + if (!isReprocessable()) { + throw new IllegalArgumentException("This capture session cannot handle " + + "reprocess requests"); + } else if (request.getReprocessableSessionId() != mId) { + throw new IllegalArgumentException("Capture request was created for another " + + "session"); + } + } + } + + } + @Override public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback, Handler handler) throws CameraAccessException { - if (request == null) { - throw new IllegalArgumentException("request must not be null"); - } else if (request.isReprocess()) { - throw new IllegalArgumentException("repeating reprocess requests are not supported"); - } + checkRepeatingRequest(request); synchronized (mDeviceImpl.mInterfaceLock) { checkNotClosed(); @@ -243,21 +299,43 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession } } + @Override + public int setSingleRepeatingRequest(CaptureRequest request, Executor executor, + CaptureCallback callback) throws CameraAccessException { + if (executor == null) { + throw new IllegalArgumentException("executor must not be null"); + } else if (callback == null) { + throw new IllegalArgumentException("callback must not be null"); + } + checkRepeatingRequest(request); + + synchronized (mDeviceImpl.mInterfaceLock) { + checkNotClosed(); + + executor = CameraDeviceImpl.checkExecutor(executor, callback); + + if (DEBUG) { + Log.v(TAG, mIdString + "setRepeatingRequest - request " + request + ", callback " + + callback + " executor" + " " + executor); + } + + return addPendingSequence(mDeviceImpl.setRepeatingRequest(request, + createCaptureCallbackProxyWithExecutor(executor, callback), mDeviceExecutor)); + } + } + + private void checkRepeatingRequest(CaptureRequest request) { + if (request == null) { + throw new IllegalArgumentException("request must not be null"); + } else if (request.isReprocess()) { + throw new IllegalArgumentException("repeating reprocess requests are not supported"); + } + } + @Override public int setRepeatingBurst(List requests, CaptureCallback callback, Handler handler) throws CameraAccessException { - if (requests == null) { - throw new IllegalArgumentException("requests must not be null"); - } else if (requests.isEmpty()) { - throw new IllegalArgumentException("requests must have at least one element"); - } - - for (CaptureRequest r : requests) { - if (r.isReprocess()) { - throw new IllegalArgumentException("repeating reprocess burst requests are not " + - "supported"); - } - } + checkRepeatingRequests(requests); synchronized (mDeviceImpl.mInterfaceLock) { checkNotClosed(); @@ -276,6 +354,48 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession } } + @Override + public int setRepeatingBurstRequests(List requests, Executor executor, + CaptureCallback callback) throws CameraAccessException { + if (executor == null) { + throw new IllegalArgumentException("executor must not be null"); + } else if (callback == null) { + throw new IllegalArgumentException("callback must not be null"); + } + checkRepeatingRequests(requests); + + synchronized (mDeviceImpl.mInterfaceLock) { + checkNotClosed(); + + executor = CameraDeviceImpl.checkExecutor(executor, callback); + + if (DEBUG) { + CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]); + Log.v(TAG, mIdString + "setRepeatingBurst - requests " + + Arrays.toString(requestArray) + ", callback " + callback + + " executor" + "" + executor); + } + + return addPendingSequence(mDeviceImpl.setRepeatingBurst(requests, + createCaptureCallbackProxyWithExecutor(executor, callback), mDeviceExecutor)); + } + } + + private void checkRepeatingRequests(List requests) { + if (requests == null) { + throw new IllegalArgumentException("requests must not be null"); + } else if (requests.isEmpty()) { + throw new IllegalArgumentException("requests must have at least one element"); + } + + for (CaptureRequest r : requests) { + if (r.isReprocess()) { + throw new IllegalArgumentException("repeating reprocess burst requests are not " + + "supported"); + } + } + } + @Override public void stopRepeating() throws CameraAccessException { synchronized (mDeviceImpl.mInterfaceLock) { @@ -462,6 +582,11 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession final Executor executor = (callback != null) ? CameraDeviceImpl.checkAndWrapHandler( handler) : null; + return createCaptureCallbackProxyWithExecutor(executor, callback); + } + + private CameraDeviceImpl.CaptureCallback createCaptureCallbackProxyWithExecutor( + Executor executor, CaptureCallback callback) { return new CameraDeviceImpl.CaptureCallback() { @Override public void onCaptureStarted(CameraDevice camera, diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java index 89f6172df6569..3494a7f2801eb 100644 --- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java @@ -192,6 +192,13 @@ public class CameraConstrainedHighSpeedCaptureSessionImpl + " this method"); } + @Override + public int captureSingleRequest(CaptureRequest request, Executor executor, + CaptureCallback listener) throws CameraAccessException { + throw new UnsupportedOperationException("Constrained high speed session doesn't support" + + " this method"); + } + @Override public int captureBurst(List requests, CaptureCallback listener, Handler handler) throws CameraAccessException { @@ -203,6 +210,17 @@ public class CameraConstrainedHighSpeedCaptureSessionImpl return mSessionImpl.captureBurst(requests, listener, handler); } + @Override + public int captureBurstRequests(List requests, Executor executor, + CaptureCallback listener) throws CameraAccessException { + if (!isConstrainedHighSpeedRequestList(requests)) { + throw new IllegalArgumentException( + "Only request lists created by createHighSpeedRequestList() can be submitted to " + + "a constrained high speed capture session"); + } + return mSessionImpl.captureBurstRequests(requests, executor, listener); + } + @Override public int setRepeatingRequest(CaptureRequest request, CaptureCallback listener, Handler handler) throws CameraAccessException { @@ -210,6 +228,13 @@ public class CameraConstrainedHighSpeedCaptureSessionImpl + " this method"); } + @Override + public int setSingleRepeatingRequest(CaptureRequest request, Executor executor, + CaptureCallback listener) throws CameraAccessException { + throw new UnsupportedOperationException("Constrained high speed session doesn't support" + + " this method"); + } + @Override public int setRepeatingBurst(List requests, CaptureCallback listener, Handler handler) throws CameraAccessException { @@ -221,6 +246,17 @@ public class CameraConstrainedHighSpeedCaptureSessionImpl return mSessionImpl.setRepeatingBurst(requests, listener, handler); } + @Override + public int setRepeatingBurstRequests(List requests, Executor executor, + CaptureCallback listener) throws CameraAccessException { + if (!isConstrainedHighSpeedRequestList(requests)) { + throw new IllegalArgumentException( + "Only request lists created by createHighSpeedRequestList() can be submitted to " + + "a constrained high speed capture session"); + } + return mSessionImpl.setRepeatingBurstRequests(requests, executor, listener); + } + @Override public void stopRepeating() throws CameraAccessException { mSessionImpl.stopRepeating(); diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index 46d4cbdeef62a..d967fbaf610cc 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -2349,7 +2349,7 @@ public class CameraDeviceImpl extends CameraDevice * *

If the callback isn't null, check the executor, otherwise pass it through.

*/ - static Executor checkExecutor(Executor executor, T callback) { + public static Executor checkExecutor(Executor executor, T callback) { return (callback != null) ? checkExecutor(executor) : executor; }