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.CaptureRequest;
25 import android.hardware.camera2.CaptureResult;
26 import android.hardware.camera2.ICameraDeviceCallbacks;
27 import android.hardware.camera2.ICameraDeviceUser;
28 import android.hardware.camera2.TotalCaptureResult;
29 import android.hardware.camera2.utils.CameraBinderDecorator;
30 import android.hardware.camera2.utils.CameraRuntimeException;
31 import android.hardware.camera2.utils.LongParcelable;
32 import android.os.Handler;
33 import android.os.IBinder;
34 import android.os.Looper;
35 import android.os.RemoteException;
36 import android.util.Log;
37 import android.util.SparseArray;
38 import android.view.Surface;
40 import java.util.AbstractMap.SimpleEntry;
41 import java.util.ArrayList;
42 import java.util.HashSet;
43 import java.util.Iterator;
44 import java.util.List;
45 import java.util.TreeSet;
48 * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
50 public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice {
52 private final String TAG;
53 private final boolean DEBUG;
55 private static final int REQUEST_ID_NONE = -1;
57 // TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
58 private ICameraDeviceUser mRemoteDevice;
60 // Lock to synchronize cross-thread access to device public interface
61 private final Object mInterfaceLock = new Object();
62 private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
64 private final StateListener mDeviceListener;
65 private volatile StateListener mSessionStateListener;
66 private final Handler mDeviceHandler;
68 private volatile boolean mClosing = false;
69 private boolean mInError = false;
70 private boolean mIdle = true;
72 /** map request IDs to listener/request data */
73 private final SparseArray<CaptureListenerHolder> mCaptureListenerMap =
74 new SparseArray<CaptureListenerHolder>();
76 private int mRepeatingRequestId = REQUEST_ID_NONE;
77 private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>();
78 // Map stream IDs to Surfaces
79 private final SparseArray<Surface> mConfiguredOutputs = new SparseArray<Surface>();
81 private final String mCameraId;
82 private final CameraCharacteristics mCharacteristics;
83 private final int mTotalPartialCount;
86 * A list tracking request and its expected last frame.
87 * Updated when calling ICameraDeviceUser methods.
89 private final List<SimpleEntry</*frameNumber*/Long, /*requestId*/Integer>>
90 mFrameNumberRequestPairs = new ArrayList<SimpleEntry<Long, Integer>>();
93 * An object tracking received frame numbers.
94 * Updated when receiving callbacks from ICameraDeviceCallbacks.
96 private final FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker();
98 private CameraCaptureSessionImpl mCurrentSession;
100 // Runnables for all state transitions, except error, which needs the
101 // error code argument
103 private final Runnable mCallOnOpened = new Runnable() {
106 StateListener sessionListener = null;
107 synchronized(mInterfaceLock) {
108 if (mRemoteDevice == null) return; // Camera already closed
110 sessionListener = mSessionStateListener;
112 if (sessionListener != null) {
113 sessionListener.onOpened(CameraDeviceImpl.this);
115 mDeviceListener.onOpened(CameraDeviceImpl.this);
119 private final Runnable mCallOnUnconfigured = new Runnable() {
122 StateListener sessionListener = null;
123 synchronized(mInterfaceLock) {
124 if (mRemoteDevice == null) return; // Camera already closed
126 sessionListener = mSessionStateListener;
128 if (sessionListener != null) {
129 sessionListener.onUnconfigured(CameraDeviceImpl.this);
131 mDeviceListener.onUnconfigured(CameraDeviceImpl.this);
135 private final Runnable mCallOnActive = new Runnable() {
138 StateListener sessionListener = null;
139 synchronized(mInterfaceLock) {
140 if (mRemoteDevice == null) return; // Camera already closed
142 sessionListener = mSessionStateListener;
144 if (sessionListener != null) {
145 sessionListener.onActive(CameraDeviceImpl.this);
147 mDeviceListener.onActive(CameraDeviceImpl.this);
151 private final Runnable mCallOnBusy = new Runnable() {
154 StateListener sessionListener = null;
155 synchronized(mInterfaceLock) {
156 if (mRemoteDevice == null) return; // Camera already closed
158 sessionListener = mSessionStateListener;
160 if (sessionListener != null) {
161 sessionListener.onBusy(CameraDeviceImpl.this);
163 mDeviceListener.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 StateListener 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 StateListener 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);
199 mDeviceListener.onIdle(CameraDeviceImpl.this);
203 private final Runnable mCallOnDisconnected = new Runnable() {
206 StateListener sessionListener = null;
207 synchronized(mInterfaceLock) {
208 if (mRemoteDevice == null) return; // Camera already closed
210 sessionListener = mSessionStateListener;
212 if (sessionListener != null) {
213 sessionListener.onDisconnected(CameraDeviceImpl.this);
215 mDeviceListener.onDisconnected(CameraDeviceImpl.this);
219 public CameraDeviceImpl(String cameraId, StateListener listener, Handler handler,
220 CameraCharacteristics characteristics) {
221 if (cameraId == null || listener == null || handler == null || characteristics == null) {
222 throw new IllegalArgumentException("Null argument given");
224 mCameraId = cameraId;
225 mDeviceListener = listener;
226 mDeviceHandler = handler;
227 mCharacteristics = characteristics;
229 final int MAX_TAG_LEN = 23;
230 String tag = String.format("CameraDevice-JV-%s", mCameraId);
231 if (tag.length() > MAX_TAG_LEN) {
232 tag = tag.substring(0, MAX_TAG_LEN);
235 DEBUG = Log.isLoggable(TAG, Log.DEBUG);
237 Integer partialCount =
238 mCharacteristics.get(CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT);
239 if (partialCount == null) {
240 // 1 means partial result is not supported.
241 mTotalPartialCount = 1;
243 mTotalPartialCount = partialCount;
247 public CameraDeviceCallbacks getCallbacks() {
251 public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
252 synchronized(mInterfaceLock) {
253 // TODO: Move from decorator to direct binder-mediated exceptions
254 // If setRemoteFailure already called, do nothing
255 if (mInError) return;
257 mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice);
259 mDeviceHandler.post(mCallOnOpened);
260 mDeviceHandler.post(mCallOnUnconfigured);
265 * Call to indicate failed connection to a remote camera device.
267 * <p>This places the camera device in the error state and informs the listener.
268 * Use in place of setRemoteDevice() when startup fails.</p>
270 public void setRemoteFailure(final CameraRuntimeException failure) {
271 int failureCode = StateListener.ERROR_CAMERA_DEVICE;
272 boolean failureIsError = true;
274 switch (failure.getReason()) {
275 case CameraAccessException.CAMERA_IN_USE:
276 failureCode = StateListener.ERROR_CAMERA_IN_USE;
278 case CameraAccessException.MAX_CAMERAS_IN_USE:
279 failureCode = StateListener.ERROR_MAX_CAMERAS_IN_USE;
281 case CameraAccessException.CAMERA_DISABLED:
282 failureCode = StateListener.ERROR_CAMERA_DISABLED;
284 case CameraAccessException.CAMERA_DISCONNECTED:
285 failureIsError = false;
287 case CameraAccessException.CAMERA_ERROR:
288 failureCode = StateListener.ERROR_CAMERA_DEVICE;
291 Log.wtf(TAG, "Unknown failure in opening camera device: " + failure.getReason());
294 final int code = failureCode;
295 final boolean isError = failureIsError;
296 synchronized(mInterfaceLock) {
298 mDeviceHandler.post(new Runnable() {
302 mDeviceListener.onError(CameraDeviceImpl.this, code);
304 mDeviceListener.onDisconnected(CameraDeviceImpl.this);
312 public String getId() {
317 public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
318 // Treat a null input the same an empty list
319 if (outputs == null) {
320 outputs = new ArrayList<Surface>();
322 synchronized(mInterfaceLock) {
323 checkIfCameraClosedOrInError();
325 HashSet<Surface> addSet = new HashSet<Surface>(outputs); // Streams to create
326 List<Integer> deleteList = new ArrayList<Integer>(); // Streams to delete
328 // Determine which streams need to be created, which to be deleted
329 for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
330 int streamId = mConfiguredOutputs.keyAt(i);
331 Surface s = mConfiguredOutputs.valueAt(i);
333 if (!outputs.contains(s)) {
334 deleteList.add(streamId);
336 addSet.remove(s); // Don't create a stream previously created
340 mDeviceHandler.post(mCallOnBusy);
346 mRemoteDevice.beginConfigure();
347 // Delete all streams first (to free up HW resources)
348 for (Integer streamId : deleteList) {
349 mRemoteDevice.deleteStream(streamId);
350 mConfiguredOutputs.delete(streamId);
353 // Add all new streams
354 for (Surface s : addSet) {
355 // TODO: remove width,height,format since we are ignoring
357 int streamId = mRemoteDevice.createStream(0, 0, 0, s);
358 mConfiguredOutputs.put(streamId, s);
361 mRemoteDevice.endConfigure();
362 } catch (CameraRuntimeException e) {
363 if (e.getReason() == CAMERA_IN_USE) {
364 throw new IllegalStateException("The camera is currently busy." +
365 " You must wait until the previous operation completes.");
369 } catch (RemoteException e) {
374 if (outputs.size() > 0) {
375 mDeviceHandler.post(mCallOnIdle);
377 mDeviceHandler.post(mCallOnUnconfigured);
383 public void createCaptureSession(List<Surface> outputs,
384 CameraCaptureSession.StateListener listener, Handler handler)
385 throws CameraAccessException {
386 synchronized(mInterfaceLock) {
388 Log.d(TAG, "createCaptureSession");
391 checkIfCameraClosedOrInError();
393 // TODO: we must be in UNCONFIGURED mode to begin with, or using another session
395 // TODO: dont block for this
396 boolean configureSuccess = true;
397 CameraAccessException pendingException = null;
399 configureOutputs(outputs); // and then block until IDLE
400 } catch (CameraAccessException e) {
401 configureSuccess = false;
402 pendingException = e;
404 Log.v(TAG, "createCaptureSession - failed with exception ", e);
408 // Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise.
409 CameraCaptureSessionImpl newSession =
410 new CameraCaptureSessionImpl(outputs, listener, handler, this, mDeviceHandler,
413 if (mCurrentSession != null) {
414 mCurrentSession.replaceSessionClose(newSession);
417 // TODO: wait until current session closes, then create the new session
418 mCurrentSession = newSession;
420 if (pendingException != null) {
421 throw pendingException;
424 mSessionStateListener = mCurrentSession.getDeviceStateListener();
429 public CaptureRequest.Builder createCaptureRequest(int templateType)
430 throws CameraAccessException {
431 synchronized(mInterfaceLock) {
432 checkIfCameraClosedOrInError();
434 CameraMetadataNative templatedRequest = new CameraMetadataNative();
437 mRemoteDevice.createDefaultRequest(templateType, /*out*/templatedRequest);
438 } catch (CameraRuntimeException e) {
440 } catch (RemoteException e) {
445 CaptureRequest.Builder builder =
446 new CaptureRequest.Builder(templatedRequest);
453 public int capture(CaptureRequest request, CaptureListener listener, Handler handler)
454 throws CameraAccessException {
456 Log.d(TAG, "calling capture");
458 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
459 requestList.add(request);
460 return submitCaptureRequest(requestList, listener, handler, /*streaming*/false);
464 public int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
465 Handler handler) throws CameraAccessException {
466 if (requests == null || requests.isEmpty()) {
467 throw new IllegalArgumentException("At least one request must be given");
469 return submitCaptureRequest(requests, listener, handler, /*streaming*/false);
473 * This method checks lastFrameNumber returned from ICameraDeviceUser methods for
474 * starting and stopping repeating request and flushing.
476 * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never
477 * sent to HAL. Then onCaptureSequenceAborted is immediately triggered.
478 * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber pair
479 * is added to the list mFrameNumberRequestPairs.</p>
481 * @param requestId the request ID of the current repeating request.
483 * @param lastFrameNumber last frame number returned from binder.
485 private void checkEarlyTriggerSequenceComplete(
486 final int requestId, final long lastFrameNumber) {
487 // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request
488 // was never sent to HAL. Should trigger onCaptureSequenceAborted immediately.
489 if (lastFrameNumber == CaptureListener.NO_FRAMES_CAPTURED) {
490 final CaptureListenerHolder holder;
491 int index = mCaptureListenerMap.indexOfKey(requestId);
492 holder = (index >= 0) ? mCaptureListenerMap.valueAt(index) : null;
493 if (holder != null) {
494 mCaptureListenerMap.removeAt(index);
496 Log.v(TAG, String.format(
497 "remove holder for requestId %d, "
498 + "because lastFrame is %d.",
499 requestId, lastFrameNumber));
503 if (holder != null) {
505 Log.v(TAG, "immediately trigger onCaptureSequenceAborted because"
506 + " request did not reach HAL");
509 Runnable resultDispatch = new Runnable() {
512 if (!CameraDeviceImpl.this.isClosed()) {
514 Log.d(TAG, String.format(
515 "early trigger sequence complete for request %d",
518 if (lastFrameNumber < Integer.MIN_VALUE
519 || lastFrameNumber > Integer.MAX_VALUE) {
520 throw new AssertionError(lastFrameNumber + " cannot be cast to int");
522 holder.getListener().onCaptureSequenceAborted(
523 CameraDeviceImpl.this,
528 holder.getHandler().post(resultDispatch);
530 Log.w(TAG, String.format(
531 "did not register listener to request %d",
535 mFrameNumberRequestPairs.add(
536 new SimpleEntry<Long, Integer>(lastFrameNumber,
541 private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureListener listener,
542 Handler handler, boolean repeating) throws CameraAccessException {
544 // Need a valid handler, or current thread needs to have a looper, if
546 handler = checkHandler(handler, listener);
548 // Make sure that there all requests have at least 1 surface; all surfaces are non-null
549 for (CaptureRequest request : requestList) {
550 if (request.getTargets().isEmpty()) {
551 throw new IllegalArgumentException(
552 "Each request must have at least one Surface target");
555 for (Surface surface : request.getTargets()) {
556 if (surface == null) {
557 throw new IllegalArgumentException("Null Surface targets are not allowed");
562 synchronized(mInterfaceLock) {
563 checkIfCameraClosedOrInError();
570 LongParcelable lastFrameNumberRef = new LongParcelable();
572 requestId = mRemoteDevice.submitRequestList(requestList, repeating,
573 /*out*/lastFrameNumberRef);
575 Log.v(TAG, "last frame number " + lastFrameNumberRef.getNumber());
577 } catch (CameraRuntimeException e) {
579 } catch (RemoteException e) {
584 if (listener != null) {
585 mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener,
586 requestList, handler, repeating));
589 Log.d(TAG, "Listen for request " + requestId + " is null");
593 long lastFrameNumber = lastFrameNumberRef.getNumber();
596 if (mRepeatingRequestId != REQUEST_ID_NONE) {
597 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
599 mRepeatingRequestId = requestId;
601 mFrameNumberRequestPairs.add(
602 new SimpleEntry<Long, Integer>(lastFrameNumber, requestId));
606 mDeviceHandler.post(mCallOnActive);
615 public int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
616 Handler handler) throws CameraAccessException {
617 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
618 requestList.add(request);
619 return submitCaptureRequest(requestList, listener, handler, /*streaming*/true);
623 public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
624 Handler handler) throws CameraAccessException {
625 if (requests == null || requests.isEmpty()) {
626 throw new IllegalArgumentException("At least one request must be given");
628 return submitCaptureRequest(requests, listener, handler, /*streaming*/true);
632 public void stopRepeating() throws CameraAccessException {
634 synchronized(mInterfaceLock) {
635 checkIfCameraClosedOrInError();
636 if (mRepeatingRequestId != REQUEST_ID_NONE) {
638 int requestId = mRepeatingRequestId;
639 mRepeatingRequestId = REQUEST_ID_NONE;
641 // Queue for deletion after in-flight requests finish
642 if (mCaptureListenerMap.get(requestId) != null) {
643 mRepeatingRequestIdDeletedList.add(requestId);
647 LongParcelable lastFrameNumberRef = new LongParcelable();
648 mRemoteDevice.cancelRequest(requestId, /*out*/lastFrameNumberRef);
649 long lastFrameNumber = lastFrameNumberRef.getNumber();
651 checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber);
653 } catch (CameraRuntimeException e) {
655 } catch (RemoteException e) {
663 private void waitUntilIdle() throws CameraAccessException {
665 synchronized(mInterfaceLock) {
666 checkIfCameraClosedOrInError();
668 if (mRepeatingRequestId != REQUEST_ID_NONE) {
669 throw new IllegalStateException("Active repeating request ongoing");
672 mRemoteDevice.waitUntilIdle();
673 } catch (CameraRuntimeException e) {
675 } catch (RemoteException e) {
683 public void flush() throws CameraAccessException {
684 synchronized(mInterfaceLock) {
685 checkIfCameraClosedOrInError();
687 mDeviceHandler.post(mCallOnBusy);
689 LongParcelable lastFrameNumberRef = new LongParcelable();
690 mRemoteDevice.flush(/*out*/lastFrameNumberRef);
691 if (mRepeatingRequestId != REQUEST_ID_NONE) {
692 long lastFrameNumber = lastFrameNumberRef.getNumber();
693 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
694 mRepeatingRequestId = REQUEST_ID_NONE;
696 } catch (CameraRuntimeException e) {
698 } catch (RemoteException e) {
706 public void close() {
707 synchronized (mInterfaceLock) {
709 if (mRemoteDevice != null) {
710 mRemoteDevice.disconnect();
712 } catch (CameraRuntimeException e) {
713 Log.e(TAG, "Exception while closing: ", e.asChecked());
714 } catch (RemoteException e) {
718 // Only want to fire the onClosed callback once;
719 // either a normal close where the remote device is valid
720 // or a close after a startup error (no remote device but in error state)
721 if (mRemoteDevice != null || mInError) {
722 mDeviceHandler.post(mCallOnClosed);
725 mRemoteDevice = null;
731 protected void finalize() throws Throwable {
740 static class CaptureListenerHolder {
742 private final boolean mRepeating;
743 private final CaptureListener mListener;
744 private final List<CaptureRequest> mRequestList;
745 private final Handler mHandler;
747 CaptureListenerHolder(CaptureListener listener, List<CaptureRequest> requestList,
748 Handler handler, boolean repeating) {
749 if (listener == null || handler == null) {
750 throw new UnsupportedOperationException(
751 "Must have a valid handler and a valid listener");
753 mRepeating = repeating;
755 mRequestList = new ArrayList<CaptureRequest>(requestList);
756 mListener = listener;
759 public boolean isRepeating() {
763 public CaptureListener getListener() {
767 public CaptureRequest getRequest(int subsequenceId) {
768 if (subsequenceId >= mRequestList.size()) {
769 throw new IllegalArgumentException(
771 "Requested subsequenceId %d is larger than request list size %d.",
772 subsequenceId, mRequestList.size()));
774 if (subsequenceId < 0) {
775 throw new IllegalArgumentException(String.format(
776 "Requested subsequenceId %d is negative", subsequenceId));
778 return mRequestList.get(subsequenceId);
783 public CaptureRequest getRequest() {
784 return getRequest(0);
787 public Handler getHandler() {
794 * This class tracks the last frame number for submitted requests.
796 public class FrameNumberTracker {
798 private long mCompletedFrameNumber = -1;
799 private final TreeSet<Long> mFutureErrorSet = new TreeSet<Long>();
801 private void update() {
802 Iterator<Long> iter = mFutureErrorSet.iterator();
803 while (iter.hasNext()) {
804 long errorFrameNumber = iter.next();
805 if (errorFrameNumber == mCompletedFrameNumber + 1) {
806 mCompletedFrameNumber++;
815 * This function is called every time when a result or an error is received.
816 * @param frameNumber: the frame number corresponding to the result or error
817 * @param isError: true if it is an error, false if it is not an error
819 public void updateTracker(long frameNumber, boolean isError) {
821 mFutureErrorSet.add(frameNumber);
824 * HAL cannot send an OnResultReceived for frame N unless it knows for
825 * sure that all frames prior to N have either errored out or completed.
826 * So if the current frame is not an error, then all previous frames
827 * should have arrived. The following line checks whether this holds.
829 if (frameNumber != mCompletedFrameNumber + 1) {
830 Log.e(TAG, String.format(
831 "result frame number %d comes out of order, should be %d + 1",
832 frameNumber, mCompletedFrameNumber));
834 mCompletedFrameNumber++;
839 public long getCompletedFrameNumber() {
840 return mCompletedFrameNumber;
845 private void checkAndFireSequenceComplete() {
846 long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
847 Iterator<SimpleEntry<Long, Integer> > iter = mFrameNumberRequestPairs.iterator();
848 while (iter.hasNext()) {
849 final SimpleEntry<Long, Integer> frameNumberRequestPair = iter.next();
850 if (frameNumberRequestPair.getKey() <= completedFrameNumber) {
852 // remove request from mCaptureListenerMap
853 final int requestId = frameNumberRequestPair.getValue();
854 final CaptureListenerHolder holder;
855 synchronized(mInterfaceLock) {
856 if (mRemoteDevice == null) {
857 Log.w(TAG, "Camera closed while checking sequences");
861 int index = mCaptureListenerMap.indexOfKey(requestId);
862 holder = (index >= 0) ? mCaptureListenerMap.valueAt(index)
864 if (holder != null) {
865 mCaptureListenerMap.removeAt(index);
867 Log.v(TAG, String.format(
868 "remove holder for requestId %d, "
869 + "because lastFrame %d is <= %d",
870 requestId, frameNumberRequestPair.getKey(),
871 completedFrameNumber));
877 // Call onCaptureSequenceCompleted
878 if (holder != null) {
879 Runnable resultDispatch = new Runnable() {
882 if (!CameraDeviceImpl.this.isClosed()){
884 Log.d(TAG, String.format(
885 "fire sequence complete for request %d",
889 long lastFrameNumber = frameNumberRequestPair.getKey();
890 if (lastFrameNumber < Integer.MIN_VALUE
891 || lastFrameNumber > Integer.MAX_VALUE) {
892 throw new AssertionError(lastFrameNumber
893 + " cannot be cast to int");
895 holder.getListener().onCaptureSequenceCompleted(
896 CameraDeviceImpl.this,
902 holder.getHandler().post(resultDispatch);
909 public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
912 // Constants below need to be kept up-to-date with
913 // frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h
917 // Error codes for onCameraError
921 * Camera has been disconnected
923 static final int ERROR_CAMERA_DISCONNECTED = 0;
926 * Camera has encountered a device-level error
927 * Matches CameraDevice.StateListener#ERROR_CAMERA_DEVICE
929 static final int ERROR_CAMERA_DEVICE = 1;
932 * Camera has encountered a service-level error
933 * Matches CameraDevice.StateListener#ERROR_CAMERA_SERVICE
935 static final int ERROR_CAMERA_SERVICE = 2;
938 public IBinder asBinder() {
943 public void onCameraError(final int errorCode, CaptureResultExtras resultExtras) {
946 synchronized(mInterfaceLock) {
947 if (mRemoteDevice == null) {
948 return; // Camera already closed
953 case ERROR_CAMERA_DISCONNECTED:
954 r = mCallOnDisconnected;
957 Log.e(TAG, "Unknown error from camera device: " + errorCode);
959 case ERROR_CAMERA_DEVICE:
960 case ERROR_CAMERA_SERVICE:
964 if (!CameraDeviceImpl.this.isClosed()) {
965 mDeviceListener.onError(CameraDeviceImpl.this, errorCode);
971 CameraDeviceImpl.this.mDeviceHandler.post(r);
973 // Fire onCaptureSequenceCompleted
975 Log.v(TAG, String.format("got error frame %d", resultExtras.getFrameNumber()));
977 mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/true);
978 checkAndFireSequenceComplete();
983 public void onCameraIdle() {
985 Log.d(TAG, "Camera now idle");
987 synchronized(mInterfaceLock) {
988 if (mRemoteDevice == null) return; // Camera already closed
990 if (!CameraDeviceImpl.this.mIdle) {
991 CameraDeviceImpl.this.mDeviceHandler.post(mCallOnIdle);
993 CameraDeviceImpl.this.mIdle = true;
998 public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) {
999 int requestId = resultExtras.getRequestId();
1001 Log.d(TAG, "Capture started for id " + requestId);
1003 final CaptureListenerHolder holder;
1005 synchronized(mInterfaceLock) {
1006 if (mRemoteDevice == null) return; // Camera already closed
1008 // Get the listener for this frame ID, if there is one
1009 holder = CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
1011 if (holder == null) {
1015 if (isClosed()) return;
1017 // Dispatch capture start notice
1018 holder.getHandler().post(
1022 if (!CameraDeviceImpl.this.isClosed()) {
1023 holder.getListener().onCaptureStarted(
1024 CameraDeviceImpl.this,
1025 holder.getRequest(resultExtras.getSubsequenceId()),
1035 public void onResultReceived(CameraMetadataNative result,
1036 CaptureResultExtras resultExtras) throws RemoteException {
1038 int requestId = resultExtras.getRequestId();
1039 long frameNumber = resultExtras.getFrameNumber();
1042 Log.v(TAG, "Received result frame " + frameNumber + " for id "
1046 synchronized(mInterfaceLock) {
1047 if (mRemoteDevice == null) return; // Camera already closed
1049 // TODO: Handle CameraCharacteristics access from CaptureResult correctly.
1050 result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE,
1051 getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE));
1053 final CaptureListenerHolder holder =
1054 CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
1056 boolean isPartialResult =
1057 (resultExtras.getPartialResultCount() < mTotalPartialCount);
1059 // Update tracker (increment counter) when it's not a partial result.
1060 if (!isPartialResult) {
1061 mFrameNumberTracker.updateTracker(frameNumber,
1065 // Check if we have a listener for this
1066 if (holder == null) {
1069 "holder is null, early return at frame "
1078 "camera is closed, early return at frame "
1084 final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
1087 Runnable resultDispatch = null;
1089 // Either send a partial result or the final capture completed result
1090 if (isPartialResult) {
1091 final CaptureResult resultAsCapture =
1092 new CaptureResult(result, request, resultExtras);
1095 resultDispatch = new Runnable() {
1098 if (!CameraDeviceImpl.this.isClosed()){
1099 holder.getListener().onCaptureProgressed(
1100 CameraDeviceImpl.this,
1107 final TotalCaptureResult resultAsCapture =
1108 new TotalCaptureResult(result, request, resultExtras);
1110 // Final capture result
1111 resultDispatch = new Runnable() {
1114 if (!CameraDeviceImpl.this.isClosed()){
1115 holder.getListener().onCaptureCompleted(
1116 CameraDeviceImpl.this,
1124 holder.getHandler().post(resultDispatch);
1126 // Fire onCaptureSequenceCompleted
1127 if (!isPartialResult) {
1128 checkAndFireSequenceComplete();
1137 * Default handler management.
1140 * If handler is null, get the current thread's
1141 * Looper to create a Handler with. If no looper exists, throw {@code IllegalArgumentException}.
1144 static Handler checkHandler(Handler handler) {
1145 if (handler == null) {
1146 Looper looper = Looper.myLooper();
1147 if (looper == null) {
1148 throw new IllegalArgumentException(
1149 "No handler given, and current thread has no looper!");
1151 handler = new Handler(looper);
1157 * Default handler management, conditional on there being a listener.
1159 * <p>If the listener isn't null, check the handler, otherwise pass it through.</p>
1161 static <T> Handler checkHandler(Handler handler, T listener) {
1162 if (listener != null) {
1163 return checkHandler(handler);
1168 private void checkIfCameraClosedOrInError() throws CameraAccessException {
1170 throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
1171 "The camera device has encountered a serious error");
1173 if (mRemoteDevice == null) {
1174 throw new IllegalStateException("CameraDevice was already closed");
1178 /** Whether the camera device has started to close (may not yet have finished) */
1179 private boolean isClosed() {
1183 private CameraCharacteristics getCharacteristics() {
1184 return mCharacteristics;