2 * Copyright (C) 2013 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package android.hardware.camera2.impl;
19 import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
21 import android.hardware.camera2.CameraAccessException;
22 import android.hardware.camera2.CameraCaptureSession;
23 import android.hardware.camera2.CameraCharacteristics;
24 import android.hardware.camera2.CameraDevice;
25 import android.hardware.camera2.CaptureRequest;
26 import android.hardware.camera2.CaptureResult;
27 import android.hardware.camera2.CaptureFailure;
28 import android.hardware.camera2.ICameraDeviceCallbacks;
29 import android.hardware.camera2.ICameraDeviceUser;
30 import android.hardware.camera2.TotalCaptureResult;
31 import android.hardware.camera2.utils.CameraBinderDecorator;
32 import android.hardware.camera2.utils.CameraRuntimeException;
33 import android.hardware.camera2.utils.LongParcelable;
34 import android.os.Handler;
35 import android.os.IBinder;
36 import android.os.Looper;
37 import android.os.RemoteException;
38 import android.util.Log;
39 import android.util.SparseArray;
40 import android.view.Surface;
42 import java.util.AbstractMap.SimpleEntry;
43 import java.util.ArrayList;
44 import java.util.HashMap;
45 import java.util.HashSet;
46 import java.util.Iterator;
47 import java.util.List;
48 import java.util.TreeSet;
51 * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
53 public class CameraDeviceImpl extends CameraDevice {
55 private final String TAG;
56 private final boolean DEBUG;
58 private static final int REQUEST_ID_NONE = -1;
60 // TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
61 private ICameraDeviceUser mRemoteDevice;
63 // Lock to synchronize cross-thread access to device public interface
64 private final Object mInterfaceLock = new Object();
65 private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
67 private final StateListener mDeviceListener;
68 private volatile StateListenerKK mSessionStateListener;
69 private final Handler mDeviceHandler;
71 private volatile boolean mClosing = false;
72 private boolean mInError = false;
73 private boolean mIdle = true;
75 /** map request IDs to listener/request data */
76 private final SparseArray<CaptureListenerHolder> mCaptureListenerMap =
77 new SparseArray<CaptureListenerHolder>();
79 private int mRepeatingRequestId = REQUEST_ID_NONE;
80 private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>();
81 // Map stream IDs to Surfaces
82 private final SparseArray<Surface> mConfiguredOutputs = new SparseArray<Surface>();
84 private final String mCameraId;
85 private final CameraCharacteristics mCharacteristics;
86 private final int mTotalPartialCount;
89 * A list tracking request and its expected last frame.
90 * Updated when calling ICameraDeviceUser methods.
92 private final List<SimpleEntry</*frameNumber*/Long, /*requestId*/Integer>>
93 mFrameNumberRequestPairs = new ArrayList<SimpleEntry<Long, Integer>>();
96 * An object tracking received frame numbers.
97 * Updated when receiving callbacks from ICameraDeviceCallbacks.
99 private final FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker();
101 private CameraCaptureSessionImpl mCurrentSession;
103 // Runnables for all state transitions, except error, which needs the
104 // error code argument
106 private final Runnable mCallOnOpened = new Runnable() {
109 StateListenerKK sessionListener = null;
110 synchronized(mInterfaceLock) {
111 if (mRemoteDevice == null) return; // Camera already closed
113 sessionListener = mSessionStateListener;
115 if (sessionListener != null) {
116 sessionListener.onOpened(CameraDeviceImpl.this);
118 mDeviceListener.onOpened(CameraDeviceImpl.this);
122 private final Runnable mCallOnUnconfigured = new Runnable() {
125 StateListenerKK sessionListener = null;
126 synchronized(mInterfaceLock) {
127 if (mRemoteDevice == null) return; // Camera already closed
129 sessionListener = mSessionStateListener;
131 if (sessionListener != null) {
132 sessionListener.onUnconfigured(CameraDeviceImpl.this);
137 private final Runnable mCallOnActive = new Runnable() {
140 StateListenerKK sessionListener = null;
141 synchronized(mInterfaceLock) {
142 if (mRemoteDevice == null) return; // Camera already closed
144 sessionListener = mSessionStateListener;
146 if (sessionListener != null) {
147 sessionListener.onActive(CameraDeviceImpl.this);
152 private final Runnable mCallOnBusy = new Runnable() {
155 StateListenerKK sessionListener = null;
156 synchronized(mInterfaceLock) {
157 if (mRemoteDevice == null) return; // Camera already closed
159 sessionListener = mSessionStateListener;
161 if (sessionListener != null) {
162 sessionListener.onBusy(CameraDeviceImpl.this);
167 private final Runnable mCallOnClosed = new Runnable() {
168 private boolean mClosedOnce = false;
173 throw new AssertionError("Don't post #onClosed more than once");
175 StateListenerKK sessionListener = null;
176 synchronized(mInterfaceLock) {
177 sessionListener = mSessionStateListener;
179 if (sessionListener != null) {
180 sessionListener.onClosed(CameraDeviceImpl.this);
182 mDeviceListener.onClosed(CameraDeviceImpl.this);
187 private final Runnable mCallOnIdle = new Runnable() {
190 StateListenerKK sessionListener = null;
191 synchronized(mInterfaceLock) {
192 if (mRemoteDevice == null) return; // Camera already closed
194 sessionListener = mSessionStateListener;
196 if (sessionListener != null) {
197 sessionListener.onIdle(CameraDeviceImpl.this);
202 private final Runnable mCallOnDisconnected = new Runnable() {
205 StateListenerKK sessionListener = null;
206 synchronized(mInterfaceLock) {
207 if (mRemoteDevice == null) return; // Camera already closed
209 sessionListener = mSessionStateListener;
211 if (sessionListener != null) {
212 sessionListener.onDisconnected(CameraDeviceImpl.this);
214 mDeviceListener.onDisconnected(CameraDeviceImpl.this);
218 public CameraDeviceImpl(String cameraId, StateListener listener, Handler handler,
219 CameraCharacteristics characteristics) {
220 if (cameraId == null || listener == null || handler == null || characteristics == null) {
221 throw new IllegalArgumentException("Null argument given");
223 mCameraId = cameraId;
224 mDeviceListener = listener;
225 mDeviceHandler = handler;
226 mCharacteristics = characteristics;
228 final int MAX_TAG_LEN = 23;
229 String tag = String.format("CameraDevice-JV-%s", mCameraId);
230 if (tag.length() > MAX_TAG_LEN) {
231 tag = tag.substring(0, MAX_TAG_LEN);
234 DEBUG = Log.isLoggable(TAG, Log.DEBUG);
236 Integer partialCount =
237 mCharacteristics.get(CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT);
238 if (partialCount == null) {
239 // 1 means partial result is not supported.
240 mTotalPartialCount = 1;
242 mTotalPartialCount = partialCount;
246 public CameraDeviceCallbacks getCallbacks() {
250 public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
251 synchronized(mInterfaceLock) {
252 // TODO: Move from decorator to direct binder-mediated exceptions
253 // If setRemoteFailure already called, do nothing
254 if (mInError) return;
256 mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice);
258 mDeviceHandler.post(mCallOnOpened);
259 mDeviceHandler.post(mCallOnUnconfigured);
264 * Call to indicate failed connection to a remote camera device.
266 * <p>This places the camera device in the error state and informs the listener.
267 * Use in place of setRemoteDevice() when startup fails.</p>
269 public void setRemoteFailure(final CameraRuntimeException failure) {
270 int failureCode = StateListener.ERROR_CAMERA_DEVICE;
271 boolean failureIsError = true;
273 switch (failure.getReason()) {
274 case CameraAccessException.CAMERA_IN_USE:
275 failureCode = StateListener.ERROR_CAMERA_IN_USE;
277 case CameraAccessException.MAX_CAMERAS_IN_USE:
278 failureCode = StateListener.ERROR_MAX_CAMERAS_IN_USE;
280 case CameraAccessException.CAMERA_DISABLED:
281 failureCode = StateListener.ERROR_CAMERA_DISABLED;
283 case CameraAccessException.CAMERA_DISCONNECTED:
284 failureIsError = false;
286 case CameraAccessException.CAMERA_ERROR:
287 failureCode = StateListener.ERROR_CAMERA_DEVICE;
290 Log.wtf(TAG, "Unknown failure in opening camera device: " + failure.getReason());
293 final int code = failureCode;
294 final boolean isError = failureIsError;
295 synchronized(mInterfaceLock) {
297 mDeviceHandler.post(new Runnable() {
301 mDeviceListener.onError(CameraDeviceImpl.this, code);
303 mDeviceListener.onDisconnected(CameraDeviceImpl.this);
311 public String getId() {
315 public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
316 // Leave this here for backwards compatibility with older code using this directly
317 configureOutputsChecked(outputs);
321 * Attempt to configure the outputs; the device goes to idle and then configures the
322 * new outputs if possible.
324 * <p>The configuration may gracefully fail, if there are too many outputs, if the formats
325 * are not supported, or if the sizes for that format is not supported. In this case this
326 * function will return {@code false} and the unconfigured callback will be fired.</p>
328 * <p>If the configuration succeeds (with 1 or more outputs), then the idle callback is fired.
329 * Unconfiguring the device always fires the idle callback.</p>
331 * @param outputs a list of one or more surfaces, or {@code null} to unconfigure
332 * @return whether or not the configuration was successful
334 * @throws CameraAccessException if there were any unexpected problems during configuration
336 public boolean configureOutputsChecked(List<Surface> outputs) throws CameraAccessException {
337 // Treat a null input the same an empty list
338 if (outputs == null) {
339 outputs = new ArrayList<Surface>();
341 boolean success = false;
343 synchronized(mInterfaceLock) {
344 checkIfCameraClosedOrInError();
346 HashSet<Surface> addSet = new HashSet<Surface>(outputs); // Streams to create
347 List<Integer> deleteList = new ArrayList<Integer>(); // Streams to delete
349 // Determine which streams need to be created, which to be deleted
350 for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
351 int streamId = mConfiguredOutputs.keyAt(i);
352 Surface s = mConfiguredOutputs.valueAt(i);
354 if (!outputs.contains(s)) {
355 deleteList.add(streamId);
357 addSet.remove(s); // Don't create a stream previously created
361 mDeviceHandler.post(mCallOnBusy);
367 mRemoteDevice.beginConfigure();
368 // Delete all streams first (to free up HW resources)
369 for (Integer streamId : deleteList) {
370 mRemoteDevice.deleteStream(streamId);
371 mConfiguredOutputs.delete(streamId);
374 // Add all new streams
375 for (Surface s : addSet) {
376 // TODO: remove width,height,format since we are ignoring
378 int streamId = mRemoteDevice.createStream(0, 0, 0, s);
379 mConfiguredOutputs.put(streamId, s);
383 mRemoteDevice.endConfigure();
385 catch (IllegalArgumentException e) {
386 // OK. camera service can reject stream config if it's not supported by HAL
387 // This is only the result of a programmer misusing the camera2 api.
388 Log.w(TAG, "Stream configuration failed");
393 } catch (CameraRuntimeException e) {
394 if (e.getReason() == CAMERA_IN_USE) {
395 throw new IllegalStateException("The camera is currently busy." +
396 " You must wait until the previous operation completes.");
400 } catch (RemoteException e) {
404 if (success && outputs.size() > 0) {
405 mDeviceHandler.post(mCallOnIdle);
407 // Always return to the 'unconfigured' state if we didn't hit a fatal error
408 mDeviceHandler.post(mCallOnUnconfigured);
417 public void createCaptureSession(List<Surface> outputs,
418 CameraCaptureSession.StateListener listener, Handler handler)
419 throws CameraAccessException {
420 synchronized(mInterfaceLock) {
422 Log.d(TAG, "createCaptureSession");
425 checkIfCameraClosedOrInError();
427 // Notify current session that it's going away, before starting camera operations
428 // After this call completes, the session is not allowed to call into CameraDeviceImpl
429 if (mCurrentSession != null) {
430 mCurrentSession.replaceSessionClose();
433 // TODO: dont block for this
434 boolean configureSuccess = true;
435 CameraAccessException pendingException = null;
437 configureSuccess = configureOutputsChecked(outputs); // and then block until IDLE
438 } catch (CameraAccessException e) {
439 configureSuccess = false;
440 pendingException = e;
442 Log.v(TAG, "createCaptureSession - failed with exception ", e);
446 // Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise.
447 CameraCaptureSessionImpl newSession =
448 new CameraCaptureSessionImpl(outputs, listener, handler, this, mDeviceHandler,
451 // TODO: wait until current session closes, then create the new session
452 mCurrentSession = newSession;
454 if (pendingException != null) {
455 throw pendingException;
458 mSessionStateListener = mCurrentSession.getDeviceStateListener();
463 * For use by backwards-compatibility code only.
465 public void setSessionListener(StateListenerKK sessionListener) {
466 synchronized(mInterfaceLock) {
467 mSessionStateListener = sessionListener;
472 public CaptureRequest.Builder createCaptureRequest(int templateType)
473 throws CameraAccessException {
474 synchronized(mInterfaceLock) {
475 checkIfCameraClosedOrInError();
477 CameraMetadataNative templatedRequest = new CameraMetadataNative();
480 mRemoteDevice.createDefaultRequest(templateType, /*out*/templatedRequest);
481 } catch (CameraRuntimeException e) {
483 } catch (RemoteException e) {
488 CaptureRequest.Builder builder =
489 new CaptureRequest.Builder(templatedRequest);
495 public int capture(CaptureRequest request, CaptureListener listener, Handler handler)
496 throws CameraAccessException {
498 Log.d(TAG, "calling capture");
500 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
501 requestList.add(request);
502 return submitCaptureRequest(requestList, listener, handler, /*streaming*/false);
505 public int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
506 Handler handler) throws CameraAccessException {
507 if (requests == null || requests.isEmpty()) {
508 throw new IllegalArgumentException("At least one request must be given");
510 return submitCaptureRequest(requests, listener, handler, /*streaming*/false);
514 * This method checks lastFrameNumber returned from ICameraDeviceUser methods for
515 * starting and stopping repeating request and flushing.
517 * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never
518 * sent to HAL. Then onCaptureSequenceAborted is immediately triggered.
519 * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber pair
520 * is added to the list mFrameNumberRequestPairs.</p>
522 * @param requestId the request ID of the current repeating request.
524 * @param lastFrameNumber last frame number returned from binder.
526 private void checkEarlyTriggerSequenceComplete(
527 final int requestId, final long lastFrameNumber) {
528 // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request
529 // was never sent to HAL. Should trigger onCaptureSequenceAborted immediately.
530 if (lastFrameNumber == CaptureListener.NO_FRAMES_CAPTURED) {
531 final CaptureListenerHolder holder;
532 int index = mCaptureListenerMap.indexOfKey(requestId);
533 holder = (index >= 0) ? mCaptureListenerMap.valueAt(index) : null;
534 if (holder != null) {
535 mCaptureListenerMap.removeAt(index);
537 Log.v(TAG, String.format(
538 "remove holder for requestId %d, "
539 + "because lastFrame is %d.",
540 requestId, lastFrameNumber));
544 if (holder != null) {
546 Log.v(TAG, "immediately trigger onCaptureSequenceAborted because"
547 + " request did not reach HAL");
550 Runnable resultDispatch = new Runnable() {
553 if (!CameraDeviceImpl.this.isClosed()) {
555 Log.d(TAG, String.format(
556 "early trigger sequence complete for request %d",
559 if (lastFrameNumber < Integer.MIN_VALUE
560 || lastFrameNumber > Integer.MAX_VALUE) {
561 throw new AssertionError(lastFrameNumber + " cannot be cast to int");
563 holder.getListener().onCaptureSequenceAborted(
564 CameraDeviceImpl.this,
569 holder.getHandler().post(resultDispatch);
571 Log.w(TAG, String.format(
572 "did not register listener to request %d",
576 mFrameNumberRequestPairs.add(
577 new SimpleEntry<Long, Integer>(lastFrameNumber,
582 private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureListener listener,
583 Handler handler, boolean repeating) throws CameraAccessException {
585 // Need a valid handler, or current thread needs to have a looper, if
587 handler = checkHandler(handler, listener);
589 // Make sure that there all requests have at least 1 surface; all surfaces are non-null
590 for (CaptureRequest request : requestList) {
591 if (request.getTargets().isEmpty()) {
592 throw new IllegalArgumentException(
593 "Each request must have at least one Surface target");
596 for (Surface surface : request.getTargets()) {
597 if (surface == null) {
598 throw new IllegalArgumentException("Null Surface targets are not allowed");
603 synchronized(mInterfaceLock) {
604 checkIfCameraClosedOrInError();
611 LongParcelable lastFrameNumberRef = new LongParcelable();
613 requestId = mRemoteDevice.submitRequestList(requestList, repeating,
614 /*out*/lastFrameNumberRef);
616 Log.v(TAG, "last frame number " + lastFrameNumberRef.getNumber());
618 } catch (CameraRuntimeException e) {
620 } catch (RemoteException e) {
625 if (listener != null) {
626 mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener,
627 requestList, handler, repeating));
630 Log.d(TAG, "Listen for request " + requestId + " is null");
634 long lastFrameNumber = lastFrameNumberRef.getNumber();
637 if (mRepeatingRequestId != REQUEST_ID_NONE) {
638 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
640 mRepeatingRequestId = requestId;
642 mFrameNumberRequestPairs.add(
643 new SimpleEntry<Long, Integer>(lastFrameNumber, requestId));
647 mDeviceHandler.post(mCallOnActive);
655 public int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
656 Handler handler) throws CameraAccessException {
657 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
658 requestList.add(request);
659 return submitCaptureRequest(requestList, listener, handler, /*streaming*/true);
662 public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
663 Handler handler) throws CameraAccessException {
664 if (requests == null || requests.isEmpty()) {
665 throw new IllegalArgumentException("At least one request must be given");
667 return submitCaptureRequest(requests, listener, handler, /*streaming*/true);
670 public void stopRepeating() throws CameraAccessException {
672 synchronized(mInterfaceLock) {
673 checkIfCameraClosedOrInError();
674 if (mRepeatingRequestId != REQUEST_ID_NONE) {
676 int requestId = mRepeatingRequestId;
677 mRepeatingRequestId = REQUEST_ID_NONE;
679 // Queue for deletion after in-flight requests finish
680 if (mCaptureListenerMap.get(requestId) != null) {
681 mRepeatingRequestIdDeletedList.add(requestId);
685 LongParcelable lastFrameNumberRef = new LongParcelable();
686 mRemoteDevice.cancelRequest(requestId, /*out*/lastFrameNumberRef);
687 long lastFrameNumber = lastFrameNumberRef.getNumber();
689 checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber);
691 } catch (CameraRuntimeException e) {
693 } catch (RemoteException e) {
701 private void waitUntilIdle() throws CameraAccessException {
703 synchronized(mInterfaceLock) {
704 checkIfCameraClosedOrInError();
706 if (mRepeatingRequestId != REQUEST_ID_NONE) {
707 throw new IllegalStateException("Active repeating request ongoing");
710 mRemoteDevice.waitUntilIdle();
711 } catch (CameraRuntimeException e) {
713 } catch (RemoteException e) {
720 public void flush() throws CameraAccessException {
721 synchronized(mInterfaceLock) {
722 checkIfCameraClosedOrInError();
724 mDeviceHandler.post(mCallOnBusy);
726 // If already idle, just do a busy->idle transition immediately, don't actually
729 mDeviceHandler.post(mCallOnIdle);
733 LongParcelable lastFrameNumberRef = new LongParcelable();
734 mRemoteDevice.flush(/*out*/lastFrameNumberRef);
735 if (mRepeatingRequestId != REQUEST_ID_NONE) {
736 long lastFrameNumber = lastFrameNumberRef.getNumber();
737 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
738 mRepeatingRequestId = REQUEST_ID_NONE;
740 } catch (CameraRuntimeException e) {
742 } catch (RemoteException e) {
750 public void close() {
751 synchronized (mInterfaceLock) {
753 if (mRemoteDevice != null) {
754 mRemoteDevice.disconnect();
756 } catch (CameraRuntimeException e) {
757 Log.e(TAG, "Exception while closing: ", e.asChecked());
758 } catch (RemoteException e) {
762 // Only want to fire the onClosed callback once;
763 // either a normal close where the remote device is valid
764 // or a close after a startup error (no remote device but in error state)
765 if (mRemoteDevice != null || mInError) {
766 mDeviceHandler.post(mCallOnClosed);
769 mRemoteDevice = null;
775 protected void finalize() throws Throwable {
785 * <p>A listener for tracking the progress of a {@link CaptureRequest}
786 * submitted to the camera device.</p>
789 public static abstract class CaptureListener {
792 * This constant is used to indicate that no images were captured for
797 public static final int NO_FRAMES_CAPTURED = -1;
800 * This method is called when the camera device has started capturing
801 * the output image for the request, at the beginning of image exposure.
803 * @see android.media.MediaActionSound
805 public void onCaptureStarted(CameraDevice camera,
806 CaptureRequest request, long timestamp) {
807 // default empty implementation
811 * This method is called when some results from an image capture are
816 public void onCapturePartial(CameraDevice camera,
817 CaptureRequest request, CaptureResult result) {
818 // default empty implementation
822 * This method is called when an image capture makes partial forward progress; some
823 * (but not all) results from an image capture are available.
826 public void onCaptureProgressed(CameraDevice camera,
827 CaptureRequest request, CaptureResult partialResult) {
828 // default empty implementation
832 * This method is called when an image capture has fully completed and all the
833 * result metadata is available.
835 public void onCaptureCompleted(CameraDevice camera,
836 CaptureRequest request, TotalCaptureResult result) {
837 // default empty implementation
841 * This method is called instead of {@link #onCaptureCompleted} when the
842 * camera device failed to produce a {@link CaptureResult} for the
845 public void onCaptureFailed(CameraDevice camera,
846 CaptureRequest request, CaptureFailure failure) {
847 // default empty implementation
851 * This method is called independently of the others in CaptureListener,
852 * when a capture sequence finishes and all {@link CaptureResult}
853 * or {@link CaptureFailure} for it have been returned via this listener.
855 public void onCaptureSequenceCompleted(CameraDevice camera,
856 int sequenceId, long frameNumber) {
857 // default empty implementation
861 * This method is called independently of the others in CaptureListener,
862 * when a capture sequence aborts before any {@link CaptureResult}
863 * or {@link CaptureFailure} for it have been returned via this listener.
865 public void onCaptureSequenceAborted(CameraDevice camera,
867 // default empty implementation
872 * A listener for notifications about the state of a camera device, adding in the callbacks that
873 * were part of the earlier KK API design, but now only used internally.
875 public static abstract class StateListenerKK extends StateListener {
877 * The method called when a camera device has no outputs configured.
880 public void onUnconfigured(CameraDevice camera) {
881 // Default empty implementation
885 * The method called when a camera device begins processing
886 * {@link CaptureRequest capture requests}.
889 public void onActive(CameraDevice camera) {
890 // Default empty implementation
894 * The method called when a camera device is busy.
897 public void onBusy(CameraDevice camera) {
898 // Default empty implementation
902 * The method called when a camera device has finished processing all
903 * submitted capture requests and has reached an idle state.
906 public void onIdle(CameraDevice camera) {
907 // Default empty implementation
911 static class CaptureListenerHolder {
913 private final boolean mRepeating;
914 private final CaptureListener mListener;
915 private final List<CaptureRequest> mRequestList;
916 private final Handler mHandler;
918 CaptureListenerHolder(CaptureListener listener, List<CaptureRequest> requestList,
919 Handler handler, boolean repeating) {
920 if (listener == null || handler == null) {
921 throw new UnsupportedOperationException(
922 "Must have a valid handler and a valid listener");
924 mRepeating = repeating;
926 mRequestList = new ArrayList<CaptureRequest>(requestList);
927 mListener = listener;
930 public boolean isRepeating() {
934 public CaptureListener getListener() {
938 public CaptureRequest getRequest(int subsequenceId) {
939 if (subsequenceId >= mRequestList.size()) {
940 throw new IllegalArgumentException(
942 "Requested subsequenceId %d is larger than request list size %d.",
943 subsequenceId, mRequestList.size()));
945 if (subsequenceId < 0) {
946 throw new IllegalArgumentException(String.format(
947 "Requested subsequenceId %d is negative", subsequenceId));
949 return mRequestList.get(subsequenceId);
954 public CaptureRequest getRequest() {
955 return getRequest(0);
958 public Handler getHandler() {
965 * This class tracks the last frame number for submitted requests.
967 public class FrameNumberTracker {
969 private long mCompletedFrameNumber = -1;
970 private final TreeSet<Long> mFutureErrorSet = new TreeSet<Long>();
971 /** Map frame numbers to list of partial results */
972 private final HashMap<Long, List<CaptureResult>> mPartialResults = new HashMap<>();
974 private void update() {
975 Iterator<Long> iter = mFutureErrorSet.iterator();
976 while (iter.hasNext()) {
977 long errorFrameNumber = iter.next();
978 if (errorFrameNumber == mCompletedFrameNumber + 1) {
979 mCompletedFrameNumber++;
988 * This function is called every time when a result or an error is received.
989 * @param frameNumber the frame number corresponding to the result or error
990 * @param isError true if it is an error, false if it is not an error
992 public void updateTracker(long frameNumber, boolean isError) {
994 mFutureErrorSet.add(frameNumber);
997 * HAL cannot send an OnResultReceived for frame N unless it knows for
998 * sure that all frames prior to N have either errored out or completed.
999 * So if the current frame is not an error, then all previous frames
1000 * should have arrived. The following line checks whether this holds.
1002 if (frameNumber != mCompletedFrameNumber + 1) {
1003 Log.e(TAG, String.format(
1004 "result frame number %d comes out of order, should be %d + 1",
1005 frameNumber, mCompletedFrameNumber));
1007 mCompletedFrameNumber++;
1013 * This function is called every time a result has been completed.
1015 * <p>It keeps a track of all the partial results already created for a particular
1018 * @param frameNumber the frame number corresponding to the result
1019 * @param result the total or partial result
1020 * @param partial {@true} if the result is partial, {@code false} if total
1022 public void updateTracker(long frameNumber, CaptureResult result, boolean partial) {
1025 // Update the total result's frame status as being successful
1026 updateTracker(frameNumber, /*isError*/false);
1027 // Don't keep a list of total results, we don't need to track them
1031 if (result == null) {
1032 // Do not record blank results; this also means there will be no total result
1033 // so it doesn't matter that the partials were not recorded
1037 // Partial results must be aggregated in-order for that frame number
1038 List<CaptureResult> partials = mPartialResults.get(frameNumber);
1039 if (partials == null) {
1040 partials = new ArrayList<>();
1041 mPartialResults.put(frameNumber, partials);
1044 partials.add(result);
1048 * Attempt to pop off all of the partial results seen so far for the {@code frameNumber}.
1050 * <p>Once popped-off, the partial results are forgotten (unless {@code updateTracker}
1051 * is called again with new partials for that frame number).</p>
1053 * @param frameNumber the frame number corresponding to the result
1054 * @return a list of partial results for that frame with at least 1 element,
1055 * or {@code null} if there were no partials recorded for that frame
1057 public List<CaptureResult> popPartialResults(long frameNumber) {
1058 return mPartialResults.remove(frameNumber);
1061 public long getCompletedFrameNumber() {
1062 return mCompletedFrameNumber;
1067 private void checkAndFireSequenceComplete() {
1068 long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
1069 Iterator<SimpleEntry<Long, Integer> > iter = mFrameNumberRequestPairs.iterator();
1070 while (iter.hasNext()) {
1071 final SimpleEntry<Long, Integer> frameNumberRequestPair = iter.next();
1072 if (frameNumberRequestPair.getKey() <= completedFrameNumber) {
1074 // remove request from mCaptureListenerMap
1075 final int requestId = frameNumberRequestPair.getValue();
1076 final CaptureListenerHolder holder;
1077 synchronized(mInterfaceLock) {
1078 if (mRemoteDevice == null) {
1079 Log.w(TAG, "Camera closed while checking sequences");
1083 int index = mCaptureListenerMap.indexOfKey(requestId);
1084 holder = (index >= 0) ? mCaptureListenerMap.valueAt(index)
1086 if (holder != null) {
1087 mCaptureListenerMap.removeAt(index);
1089 Log.v(TAG, String.format(
1090 "remove holder for requestId %d, "
1091 + "because lastFrame %d is <= %d",
1092 requestId, frameNumberRequestPair.getKey(),
1093 completedFrameNumber));
1099 // Call onCaptureSequenceCompleted
1100 if (holder != null) {
1101 Runnable resultDispatch = new Runnable() {
1104 if (!CameraDeviceImpl.this.isClosed()){
1106 Log.d(TAG, String.format(
1107 "fire sequence complete for request %d",
1111 long lastFrameNumber = frameNumberRequestPair.getKey();
1112 if (lastFrameNumber < Integer.MIN_VALUE
1113 || lastFrameNumber > Integer.MAX_VALUE) {
1114 throw new AssertionError(lastFrameNumber
1115 + " cannot be cast to int");
1117 holder.getListener().onCaptureSequenceCompleted(
1118 CameraDeviceImpl.this,
1124 holder.getHandler().post(resultDispatch);
1131 public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
1134 // Constants below need to be kept up-to-date with
1135 // frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h
1139 // Error codes for onCameraError
1143 * Camera has been disconnected
1145 static final int ERROR_CAMERA_DISCONNECTED = 0;
1148 * Camera has encountered a device-level error
1149 * Matches CameraDevice.StateListener#ERROR_CAMERA_DEVICE
1151 static final int ERROR_CAMERA_DEVICE = 1;
1154 * Camera has encountered a service-level error
1155 * Matches CameraDevice.StateListener#ERROR_CAMERA_SERVICE
1157 static final int ERROR_CAMERA_SERVICE = 2;
1160 * Camera has encountered an error processing a single request.
1162 static final int ERROR_CAMERA_REQUEST = 3;
1165 * Camera has encountered an error producing metadata for a single capture
1167 static final int ERROR_CAMERA_RESULT = 4;
1170 * Camera has encountered an error producing an image buffer for a single capture
1172 static final int ERROR_CAMERA_BUFFER = 5;
1175 public IBinder asBinder() {
1180 public void onDeviceError(final int errorCode, CaptureResultExtras resultExtras) {
1182 Log.d(TAG, String.format(
1183 "Device error received, code %d, frame number %d, request ID %d, subseq ID %d",
1184 errorCode, resultExtras.getFrameNumber(), resultExtras.getRequestId(),
1185 resultExtras.getSubsequenceId()));
1188 synchronized(mInterfaceLock) {
1189 if (mRemoteDevice == null) {
1190 return; // Camera already closed
1193 switch (errorCode) {
1194 case ERROR_CAMERA_DISCONNECTED:
1195 CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected);
1198 Log.e(TAG, "Unknown error from camera device: " + errorCode);
1200 case ERROR_CAMERA_DEVICE:
1201 case ERROR_CAMERA_SERVICE:
1203 Runnable r = new Runnable() {
1206 if (!CameraDeviceImpl.this.isClosed()) {
1207 mDeviceListener.onError(CameraDeviceImpl.this, errorCode);
1211 CameraDeviceImpl.this.mDeviceHandler.post(r);
1213 case ERROR_CAMERA_REQUEST:
1214 case ERROR_CAMERA_RESULT:
1215 case ERROR_CAMERA_BUFFER:
1216 onCaptureErrorLocked(errorCode, resultExtras);
1223 public void onDeviceIdle() {
1225 Log.d(TAG, "Camera now idle");
1227 synchronized(mInterfaceLock) {
1228 if (mRemoteDevice == null) return; // Camera already closed
1230 if (!CameraDeviceImpl.this.mIdle) {
1231 CameraDeviceImpl.this.mDeviceHandler.post(mCallOnIdle);
1233 CameraDeviceImpl.this.mIdle = true;
1238 public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) {
1239 int requestId = resultExtras.getRequestId();
1241 Log.d(TAG, "Capture started for id " + requestId);
1243 final CaptureListenerHolder holder;
1245 synchronized(mInterfaceLock) {
1246 if (mRemoteDevice == null) return; // Camera already closed
1248 // Get the listener for this frame ID, if there is one
1249 holder = CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
1251 if (holder == null) {
1255 if (isClosed()) return;
1257 // Dispatch capture start notice
1258 holder.getHandler().post(
1262 if (!CameraDeviceImpl.this.isClosed()) {
1263 holder.getListener().onCaptureStarted(
1264 CameraDeviceImpl.this,
1265 holder.getRequest(resultExtras.getSubsequenceId()),
1275 public void onResultReceived(CameraMetadataNative result,
1276 CaptureResultExtras resultExtras) throws RemoteException {
1278 int requestId = resultExtras.getRequestId();
1279 long frameNumber = resultExtras.getFrameNumber();
1282 Log.v(TAG, "Received result frame " + frameNumber + " for id "
1286 synchronized(mInterfaceLock) {
1287 if (mRemoteDevice == null) return; // Camera already closed
1289 // TODO: Handle CameraCharacteristics access from CaptureResult correctly.
1290 result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE,
1291 getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE));
1293 final CaptureListenerHolder holder =
1294 CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
1296 boolean isPartialResult =
1297 (resultExtras.getPartialResultCount() < mTotalPartialCount);
1299 // Check if we have a listener for this
1300 if (holder == null) {
1303 "holder is null, early return at frame "
1307 mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult);
1315 "camera is closed, early return at frame "
1319 mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult);
1323 final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
1325 Runnable resultDispatch = null;
1327 CaptureResult finalResult;
1329 // Either send a partial result or the final capture completed result
1330 if (isPartialResult) {
1331 final CaptureResult resultAsCapture =
1332 new CaptureResult(result, request, resultExtras);
1335 resultDispatch = new Runnable() {
1338 if (!CameraDeviceImpl.this.isClosed()){
1339 holder.getListener().onCaptureProgressed(
1340 CameraDeviceImpl.this,
1347 finalResult = resultAsCapture;
1349 List<CaptureResult> partialResults =
1350 mFrameNumberTracker.popPartialResults(frameNumber);
1352 final TotalCaptureResult resultAsCapture =
1353 new TotalCaptureResult(result, request, resultExtras, partialResults);
1355 // Final capture result
1356 resultDispatch = new Runnable() {
1359 if (!CameraDeviceImpl.this.isClosed()){
1360 holder.getListener().onCaptureCompleted(
1361 CameraDeviceImpl.this,
1368 finalResult = resultAsCapture;
1371 holder.getHandler().post(resultDispatch);
1373 // Collect the partials for a total result; or mark the frame as totally completed
1374 mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult);
1376 // Fire onCaptureSequenceCompleted
1377 if (!isPartialResult) {
1378 checkAndFireSequenceComplete();
1384 * Called by onDeviceError for handling single-capture failures.
1386 private void onCaptureErrorLocked(int errorCode, CaptureResultExtras resultExtras) {
1388 final int requestId = resultExtras.getRequestId();
1389 final int subsequenceId = resultExtras.getSubsequenceId();
1390 final long frameNumber = resultExtras.getFrameNumber();
1391 final CaptureListenerHolder holder =
1392 CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
1394 final CaptureRequest request = holder.getRequest(subsequenceId);
1396 // No way to report buffer errors right now
1397 if (errorCode == ERROR_CAMERA_BUFFER) {
1398 Log.e(TAG, String.format("Lost output buffer reported for frame %d", frameNumber));
1402 boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT);
1404 // This is only approximate - exact handling needs the camera service and HAL to
1405 // disambiguate between request failures to due abort and due to real errors.
1406 // For now, assume that if the session believes we're mid-abort, then the error
1408 int reason = (mCurrentSession != null && mCurrentSession.isAborting()) ?
1409 CaptureFailure.REASON_FLUSHED :
1410 CaptureFailure.REASON_ERROR;
1412 final CaptureFailure failure = new CaptureFailure(
1415 /*dropped*/ mayHaveBuffers,
1419 Runnable failureDispatch = new Runnable() {
1422 if (!CameraDeviceImpl.this.isClosed()){
1423 holder.getListener().onCaptureFailed(
1424 CameraDeviceImpl.this,
1430 holder.getHandler().post(failureDispatch);
1432 // Fire onCaptureSequenceCompleted if appropriate
1434 Log.v(TAG, String.format("got error frame %d", frameNumber));
1436 mFrameNumberTracker.updateTracker(frameNumber, /*error*/true);
1437 checkAndFireSequenceComplete();
1440 } // public class CameraDeviceCallbacks
1443 * Default handler management.
1446 * If handler is null, get the current thread's
1447 * Looper to create a Handler with. If no looper exists, throw {@code IllegalArgumentException}.
1450 static Handler checkHandler(Handler handler) {
1451 if (handler == null) {
1452 Looper looper = Looper.myLooper();
1453 if (looper == null) {
1454 throw new IllegalArgumentException(
1455 "No handler given, and current thread has no looper!");
1457 handler = new Handler(looper);
1463 * Default handler management, conditional on there being a listener.
1465 * <p>If the listener isn't null, check the handler, otherwise pass it through.</p>
1467 static <T> Handler checkHandler(Handler handler, T listener) {
1468 if (listener != null) {
1469 return checkHandler(handler);
1474 private void checkIfCameraClosedOrInError() throws CameraAccessException {
1476 throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
1477 "The camera device has encountered a serious error");
1479 if (mRemoteDevice == null) {
1480 throw new IllegalStateException("CameraDevice was already closed");
1484 /** Whether the camera device has started to close (may not yet have finished) */
1485 private boolean isClosed() {
1489 private CameraCharacteristics getCharacteristics() {
1490 return mCharacteristics;