OSDN Git Service

Camera2: Fix null-listener capture for CameraCaptureSession
[android-x86/frameworks-base.git] / core / java / android / hardware / camera2 / impl / CameraDeviceImpl.java
1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package android.hardware.camera2.impl;
18
19 import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
20
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;
39
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;
46
47 /**
48  * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
49  */
50 public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice {
51
52     private final String TAG;
53     private final boolean DEBUG;
54
55     private static final int REQUEST_ID_NONE = -1;
56
57     // TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
58     private ICameraDeviceUser mRemoteDevice;
59
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();
63
64     private final StateListener mDeviceListener;
65     private volatile StateListener mSessionStateListener;
66     private final Handler mDeviceHandler;
67
68     private volatile boolean mClosing = false;
69     private boolean mInError = false;
70     private boolean mIdle = true;
71
72     /** map request IDs to listener/request data */
73     private final SparseArray<CaptureListenerHolder> mCaptureListenerMap =
74             new SparseArray<CaptureListenerHolder>();
75
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>();
80
81     private final String mCameraId;
82     private final CameraCharacteristics mCharacteristics;
83     private final int mTotalPartialCount;
84
85     /**
86      * A list tracking request and its expected last frame.
87      * Updated when calling ICameraDeviceUser methods.
88      */
89     private final List<SimpleEntry</*frameNumber*/Long, /*requestId*/Integer>>
90             mFrameNumberRequestPairs = new ArrayList<SimpleEntry<Long, Integer>>();
91
92     /**
93      * An object tracking received frame numbers.
94      * Updated when receiving callbacks from ICameraDeviceCallbacks.
95      */
96     private final FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker();
97
98     private CameraCaptureSessionImpl mCurrentSession;
99
100     // Runnables for all state transitions, except error, which needs the
101     // error code argument
102
103     private final Runnable mCallOnOpened = new Runnable() {
104         @Override
105         public void run() {
106             StateListener sessionListener = null;
107             synchronized(mInterfaceLock) {
108                 if (mRemoteDevice == null) return; // Camera already closed
109
110                 sessionListener = mSessionStateListener;
111             }
112             if (sessionListener != null) {
113                 sessionListener.onOpened(CameraDeviceImpl.this);
114             }
115             mDeviceListener.onOpened(CameraDeviceImpl.this);
116         }
117     };
118
119     private final Runnable mCallOnUnconfigured = new Runnable() {
120         @Override
121         public void run() {
122             StateListener sessionListener = null;
123             synchronized(mInterfaceLock) {
124                 if (mRemoteDevice == null) return; // Camera already closed
125
126                 sessionListener = mSessionStateListener;
127             }
128             if (sessionListener != null) {
129                 sessionListener.onUnconfigured(CameraDeviceImpl.this);
130             }
131             mDeviceListener.onUnconfigured(CameraDeviceImpl.this);
132         }
133     };
134
135     private final Runnable mCallOnActive = new Runnable() {
136         @Override
137         public void run() {
138             StateListener sessionListener = null;
139             synchronized(mInterfaceLock) {
140                 if (mRemoteDevice == null) return; // Camera already closed
141
142                 sessionListener = mSessionStateListener;
143             }
144             if (sessionListener != null) {
145                 sessionListener.onActive(CameraDeviceImpl.this);
146             }
147             mDeviceListener.onActive(CameraDeviceImpl.this);
148         }
149     };
150
151     private final Runnable mCallOnBusy = new Runnable() {
152         @Override
153         public void run() {
154             StateListener sessionListener = null;
155             synchronized(mInterfaceLock) {
156                 if (mRemoteDevice == null) return; // Camera already closed
157
158                 sessionListener = mSessionStateListener;
159             }
160             if (sessionListener != null) {
161                 sessionListener.onBusy(CameraDeviceImpl.this);
162             }
163             mDeviceListener.onBusy(CameraDeviceImpl.this);
164         }
165     };
166
167     private final Runnable mCallOnClosed = new Runnable() {
168         private boolean mClosedOnce = false;
169
170         @Override
171         public void run() {
172             if (mClosedOnce) {
173                 throw new AssertionError("Don't post #onClosed more than once");
174             }
175             StateListener sessionListener = null;
176             synchronized(mInterfaceLock) {
177                 sessionListener = mSessionStateListener;
178             }
179             if (sessionListener != null) {
180                 sessionListener.onClosed(CameraDeviceImpl.this);
181             }
182             mDeviceListener.onClosed(CameraDeviceImpl.this);
183             mClosedOnce = true;
184         }
185     };
186
187     private final Runnable mCallOnIdle = new Runnable() {
188         @Override
189         public void run() {
190             StateListener sessionListener = null;
191             synchronized(mInterfaceLock) {
192                 if (mRemoteDevice == null) return; // Camera already closed
193
194                 sessionListener = mSessionStateListener;
195             }
196             if (sessionListener != null) {
197                 sessionListener.onIdle(CameraDeviceImpl.this);
198             }
199             mDeviceListener.onIdle(CameraDeviceImpl.this);
200         }
201     };
202
203     private final Runnable mCallOnDisconnected = new Runnable() {
204         @Override
205         public void run() {
206             StateListener sessionListener = null;
207             synchronized(mInterfaceLock) {
208                 if (mRemoteDevice == null) return; // Camera already closed
209
210                 sessionListener = mSessionStateListener;
211             }
212             if (sessionListener != null) {
213                 sessionListener.onDisconnected(CameraDeviceImpl.this);
214             }
215             mDeviceListener.onDisconnected(CameraDeviceImpl.this);
216         }
217     };
218
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");
223         }
224         mCameraId = cameraId;
225         mDeviceListener = listener;
226         mDeviceHandler = handler;
227         mCharacteristics = characteristics;
228
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);
233         }
234         TAG = tag;
235         DEBUG = Log.isLoggable(TAG, Log.DEBUG);
236
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;
242         } else {
243             mTotalPartialCount = partialCount;
244         }
245     }
246
247     public CameraDeviceCallbacks getCallbacks() {
248         return mCallbacks;
249     }
250
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;
256
257             mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice);
258
259             mDeviceHandler.post(mCallOnOpened);
260             mDeviceHandler.post(mCallOnUnconfigured);
261         }
262     }
263
264     /**
265      * Call to indicate failed connection to a remote camera device.
266      *
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>
269      */
270     public void setRemoteFailure(final CameraRuntimeException failure) {
271         int failureCode = StateListener.ERROR_CAMERA_DEVICE;
272         boolean failureIsError = true;
273
274         switch (failure.getReason()) {
275             case CameraAccessException.CAMERA_IN_USE:
276                 failureCode = StateListener.ERROR_CAMERA_IN_USE;
277                 break;
278             case CameraAccessException.MAX_CAMERAS_IN_USE:
279                 failureCode = StateListener.ERROR_MAX_CAMERAS_IN_USE;
280                 break;
281             case CameraAccessException.CAMERA_DISABLED:
282                 failureCode = StateListener.ERROR_CAMERA_DISABLED;
283                 break;
284             case CameraAccessException.CAMERA_DISCONNECTED:
285                 failureIsError = false;
286                 break;
287             case CameraAccessException.CAMERA_ERROR:
288                 failureCode = StateListener.ERROR_CAMERA_DEVICE;
289                 break;
290             default:
291                 Log.wtf(TAG, "Unknown failure in opening camera device: " + failure.getReason());
292                 break;
293         }
294         final int code = failureCode;
295         final boolean isError = failureIsError;
296         synchronized(mInterfaceLock) {
297             mInError = true;
298             mDeviceHandler.post(new Runnable() {
299                 @Override
300                 public void run() {
301                     if (isError) {
302                         mDeviceListener.onError(CameraDeviceImpl.this, code);
303                     } else {
304                         mDeviceListener.onDisconnected(CameraDeviceImpl.this);
305                     }
306                 }
307             });
308         }
309     }
310
311     @Override
312     public String getId() {
313         return mCameraId;
314     }
315
316     @Override
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>();
321         }
322         synchronized(mInterfaceLock) {
323             checkIfCameraClosedOrInError();
324
325             HashSet<Surface> addSet = new HashSet<Surface>(outputs);    // Streams to create
326             List<Integer> deleteList = new ArrayList<Integer>();        // Streams to delete
327
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);
332
333                 if (!outputs.contains(s)) {
334                     deleteList.add(streamId);
335                 } else {
336                     addSet.remove(s);  // Don't create a stream previously created
337                 }
338             }
339
340             mDeviceHandler.post(mCallOnBusy);
341             stopRepeating();
342
343             try {
344                 waitUntilIdle();
345
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);
351                 }
352
353                 // Add all new streams
354                 for (Surface s : addSet) {
355                     // TODO: remove width,height,format since we are ignoring
356                     // it.
357                     int streamId = mRemoteDevice.createStream(0, 0, 0, s);
358                     mConfiguredOutputs.put(streamId, s);
359                 }
360
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.");
366                 }
367
368                 throw e.asChecked();
369             } catch (RemoteException e) {
370                 // impossible
371                 return;
372             }
373
374             if (outputs.size() > 0) {
375                 mDeviceHandler.post(mCallOnIdle);
376             } else {
377                 mDeviceHandler.post(mCallOnUnconfigured);
378             }
379         }
380     }
381
382     @Override
383     public void createCaptureSession(List<Surface> outputs,
384             CameraCaptureSession.StateListener listener, Handler handler)
385             throws CameraAccessException {
386         synchronized(mInterfaceLock) {
387             if (DEBUG) {
388                 Log.d(TAG, "createCaptureSession");
389             }
390
391             checkIfCameraClosedOrInError();
392
393             // TODO: we must be in UNCONFIGURED mode to begin with, or using another session
394
395             // TODO: dont block for this
396             boolean configureSuccess = true;
397             CameraAccessException pendingException = null;
398             try {
399                 configureOutputs(outputs); // and then block until IDLE
400             } catch (CameraAccessException e) {
401                 configureSuccess = false;
402                 pendingException = e;
403                 if (DEBUG) {
404                     Log.v(TAG, "createCaptureSession - failed with exception ", e);
405                 }
406             }
407
408             // Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise.
409             CameraCaptureSessionImpl newSession =
410                     new CameraCaptureSessionImpl(outputs, listener, handler, this, mDeviceHandler,
411                             configureSuccess);
412
413             if (mCurrentSession != null) {
414                 mCurrentSession.replaceSessionClose(newSession);
415             }
416
417             // TODO: wait until current session closes, then create the new session
418             mCurrentSession = newSession;
419
420             if (pendingException != null) {
421                 throw pendingException;
422             }
423
424             mSessionStateListener = mCurrentSession.getDeviceStateListener();
425         }
426     }
427
428     @Override
429     public CaptureRequest.Builder createCaptureRequest(int templateType)
430             throws CameraAccessException {
431         synchronized(mInterfaceLock) {
432             checkIfCameraClosedOrInError();
433
434             CameraMetadataNative templatedRequest = new CameraMetadataNative();
435
436             try {
437                 mRemoteDevice.createDefaultRequest(templateType, /*out*/templatedRequest);
438             } catch (CameraRuntimeException e) {
439                 throw e.asChecked();
440             } catch (RemoteException e) {
441                 // impossible
442                 return null;
443             }
444
445             CaptureRequest.Builder builder =
446                     new CaptureRequest.Builder(templatedRequest);
447
448             return builder;
449         }
450     }
451
452     @Override
453     public int capture(CaptureRequest request, CaptureListener listener, Handler handler)
454             throws CameraAccessException {
455         if (DEBUG) {
456             Log.d(TAG, "calling capture");
457         }
458         List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
459         requestList.add(request);
460         return submitCaptureRequest(requestList, listener, handler, /*streaming*/false);
461     }
462
463     @Override
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");
468         }
469         return submitCaptureRequest(requests, listener, handler, /*streaming*/false);
470     }
471
472     /**
473      * This method checks lastFrameNumber returned from ICameraDeviceUser methods for
474      * starting and stopping repeating request and flushing.
475      *
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>
480      *
481      * @param requestId the request ID of the current repeating request.
482      *
483      * @param lastFrameNumber last frame number returned from binder.
484      */
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);
495                 if (DEBUG) {
496                     Log.v(TAG, String.format(
497                             "remove holder for requestId %d, "
498                             + "because lastFrame is %d.",
499                             requestId, lastFrameNumber));
500                 }
501             }
502
503             if (holder != null) {
504                 if (DEBUG) {
505                     Log.v(TAG, "immediately trigger onCaptureSequenceAborted because"
506                             + " request did not reach HAL");
507                 }
508
509                 Runnable resultDispatch = new Runnable() {
510                     @Override
511                     public void run() {
512                         if (!CameraDeviceImpl.this.isClosed()) {
513                             if (DEBUG) {
514                                 Log.d(TAG, String.format(
515                                         "early trigger sequence complete for request %d",
516                                         requestId));
517                             }
518                             if (lastFrameNumber < Integer.MIN_VALUE
519                                     || lastFrameNumber > Integer.MAX_VALUE) {
520                                 throw new AssertionError(lastFrameNumber + " cannot be cast to int");
521                             }
522                             holder.getListener().onCaptureSequenceAborted(
523                                     CameraDeviceImpl.this,
524                                     requestId);
525                         }
526                     }
527                 };
528                 holder.getHandler().post(resultDispatch);
529             } else {
530                 Log.w(TAG, String.format(
531                         "did not register listener to request %d",
532                         requestId));
533             }
534         } else {
535             mFrameNumberRequestPairs.add(
536                     new SimpleEntry<Long, Integer>(lastFrameNumber,
537                             requestId));
538         }
539     }
540
541     private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureListener listener,
542             Handler handler, boolean repeating) throws CameraAccessException {
543
544         // Need a valid handler, or current thread needs to have a looper, if
545         // listener is valid
546         handler = checkHandler(handler, listener);
547
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");
553             }
554
555             for (Surface surface : request.getTargets()) {
556                 if (surface == null) {
557                     throw new IllegalArgumentException("Null Surface targets are not allowed");
558                 }
559             }
560         }
561
562         synchronized(mInterfaceLock) {
563             checkIfCameraClosedOrInError();
564             int requestId;
565
566             if (repeating) {
567                 stopRepeating();
568             }
569
570             LongParcelable lastFrameNumberRef = new LongParcelable();
571             try {
572                 requestId = mRemoteDevice.submitRequestList(requestList, repeating,
573                         /*out*/lastFrameNumberRef);
574                 if (DEBUG) {
575                     Log.v(TAG, "last frame number " + lastFrameNumberRef.getNumber());
576                 }
577             } catch (CameraRuntimeException e) {
578                 throw e.asChecked();
579             } catch (RemoteException e) {
580                 // impossible
581                 return -1;
582             }
583
584             if (listener != null) {
585                 mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener,
586                         requestList, handler, repeating));
587             } else {
588                 if (DEBUG) {
589                     Log.d(TAG, "Listen for request " + requestId + " is null");
590                 }
591             }
592
593             long lastFrameNumber = lastFrameNumberRef.getNumber();
594
595             if (repeating) {
596                 if (mRepeatingRequestId != REQUEST_ID_NONE) {
597                     checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
598                 }
599                 mRepeatingRequestId = requestId;
600             } else {
601                 mFrameNumberRequestPairs.add(
602                         new SimpleEntry<Long, Integer>(lastFrameNumber, requestId));
603             }
604
605             if (mIdle) {
606                 mDeviceHandler.post(mCallOnActive);
607             }
608             mIdle = false;
609
610             return requestId;
611         }
612     }
613
614     @Override
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);
620     }
621
622     @Override
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");
627         }
628         return submitCaptureRequest(requests, listener, handler, /*streaming*/true);
629     }
630
631     @Override
632     public void stopRepeating() throws CameraAccessException {
633
634         synchronized(mInterfaceLock) {
635             checkIfCameraClosedOrInError();
636             if (mRepeatingRequestId != REQUEST_ID_NONE) {
637
638                 int requestId = mRepeatingRequestId;
639                 mRepeatingRequestId = REQUEST_ID_NONE;
640
641                 // Queue for deletion after in-flight requests finish
642                 if (mCaptureListenerMap.get(requestId) != null) {
643                     mRepeatingRequestIdDeletedList.add(requestId);
644                 }
645
646                 try {
647                     LongParcelable lastFrameNumberRef = new LongParcelable();
648                     mRemoteDevice.cancelRequest(requestId, /*out*/lastFrameNumberRef);
649                     long lastFrameNumber = lastFrameNumberRef.getNumber();
650
651                     checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber);
652
653                 } catch (CameraRuntimeException e) {
654                     throw e.asChecked();
655                 } catch (RemoteException e) {
656                     // impossible
657                     return;
658                 }
659             }
660         }
661     }
662
663     private void waitUntilIdle() throws CameraAccessException {
664
665         synchronized(mInterfaceLock) {
666             checkIfCameraClosedOrInError();
667
668             if (mRepeatingRequestId != REQUEST_ID_NONE) {
669                 throw new IllegalStateException("Active repeating request ongoing");
670             }
671             try {
672                 mRemoteDevice.waitUntilIdle();
673             } catch (CameraRuntimeException e) {
674                 throw e.asChecked();
675             } catch (RemoteException e) {
676                 // impossible
677                 return;
678             }
679         }
680     }
681
682     @Override
683     public void flush() throws CameraAccessException {
684         synchronized(mInterfaceLock) {
685             checkIfCameraClosedOrInError();
686
687             mDeviceHandler.post(mCallOnBusy);
688             try {
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;
695                 }
696             } catch (CameraRuntimeException e) {
697                 throw e.asChecked();
698             } catch (RemoteException e) {
699                 // impossible
700                 return;
701             }
702         }
703     }
704
705     @Override
706     public void close() {
707         synchronized (mInterfaceLock) {
708             try {
709                 if (mRemoteDevice != null) {
710                     mRemoteDevice.disconnect();
711                 }
712             } catch (CameraRuntimeException e) {
713                 Log.e(TAG, "Exception while closing: ", e.asChecked());
714             } catch (RemoteException e) {
715                 // impossible
716             }
717
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);
723             }
724
725             mRemoteDevice = null;
726             mInError = false;
727         }
728     }
729
730     @Override
731     protected void finalize() throws Throwable {
732         try {
733             close();
734         }
735         finally {
736             super.finalize();
737         }
738     }
739
740     static class CaptureListenerHolder {
741
742         private final boolean mRepeating;
743         private final CaptureListener mListener;
744         private final List<CaptureRequest> mRequestList;
745         private final Handler mHandler;
746
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");
752             }
753             mRepeating = repeating;
754             mHandler = handler;
755             mRequestList = new ArrayList<CaptureRequest>(requestList);
756             mListener = listener;
757         }
758
759         public boolean isRepeating() {
760             return mRepeating;
761         }
762
763         public CaptureListener getListener() {
764             return mListener;
765         }
766
767         public CaptureRequest getRequest(int subsequenceId) {
768             if (subsequenceId >= mRequestList.size()) {
769                 throw new IllegalArgumentException(
770                         String.format(
771                                 "Requested subsequenceId %d is larger than request list size %d.",
772                                 subsequenceId, mRequestList.size()));
773             } else {
774                 if (subsequenceId < 0) {
775                     throw new IllegalArgumentException(String.format(
776                             "Requested subsequenceId %d is negative", subsequenceId));
777                 } else {
778                     return mRequestList.get(subsequenceId);
779                 }
780             }
781         }
782
783         public CaptureRequest getRequest() {
784             return getRequest(0);
785         }
786
787         public Handler getHandler() {
788             return mHandler;
789         }
790
791     }
792
793     /**
794      * This class tracks the last frame number for submitted requests.
795      */
796     public class FrameNumberTracker {
797
798         private long mCompletedFrameNumber = -1;
799         private final TreeSet<Long> mFutureErrorSet = new TreeSet<Long>();
800
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++;
807                     iter.remove();
808                 } else {
809                     break;
810                 }
811             }
812         }
813
814         /**
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
818          */
819         public void updateTracker(long frameNumber, boolean isError) {
820             if (isError) {
821                 mFutureErrorSet.add(frameNumber);
822             } else {
823                 /**
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.
828                  */
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));
833                 }
834                 mCompletedFrameNumber++;
835             }
836             update();
837         }
838
839         public long getCompletedFrameNumber() {
840             return mCompletedFrameNumber;
841         }
842
843     }
844
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) {
851
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");
858                         return;
859                     }
860
861                     int index = mCaptureListenerMap.indexOfKey(requestId);
862                     holder = (index >= 0) ? mCaptureListenerMap.valueAt(index)
863                             : null;
864                     if (holder != null) {
865                         mCaptureListenerMap.removeAt(index);
866                         if (DEBUG) {
867                             Log.v(TAG, String.format(
868                                     "remove holder for requestId %d, "
869                                     + "because lastFrame %d is <= %d",
870                                     requestId, frameNumberRequestPair.getKey(),
871                                     completedFrameNumber));
872                         }
873                     }
874                 }
875                 iter.remove();
876
877                 // Call onCaptureSequenceCompleted
878                 if (holder != null) {
879                     Runnable resultDispatch = new Runnable() {
880                         @Override
881                         public void run() {
882                             if (!CameraDeviceImpl.this.isClosed()){
883                                 if (DEBUG) {
884                                     Log.d(TAG, String.format(
885                                             "fire sequence complete for request %d",
886                                             requestId));
887                                 }
888
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");
894                                 }
895                                 holder.getListener().onCaptureSequenceCompleted(
896                                     CameraDeviceImpl.this,
897                                     requestId,
898                                     lastFrameNumber);
899                             }
900                         }
901                     };
902                     holder.getHandler().post(resultDispatch);
903                 }
904
905             }
906         }
907     }
908
909     public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
910
911         //
912         // Constants below need to be kept up-to-date with
913         // frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h
914         //
915
916         //
917         // Error codes for onCameraError
918         //
919
920         /**
921          * Camera has been disconnected
922          */
923         static final int ERROR_CAMERA_DISCONNECTED = 0;
924
925         /**
926          * Camera has encountered a device-level error
927          * Matches CameraDevice.StateListener#ERROR_CAMERA_DEVICE
928          */
929         static final int ERROR_CAMERA_DEVICE = 1;
930
931         /**
932          * Camera has encountered a service-level error
933          * Matches CameraDevice.StateListener#ERROR_CAMERA_SERVICE
934          */
935         static final int ERROR_CAMERA_SERVICE = 2;
936
937         @Override
938         public IBinder asBinder() {
939             return this;
940         }
941
942         @Override
943         public void onCameraError(final int errorCode, CaptureResultExtras resultExtras) {
944             Runnable r = null;
945
946             synchronized(mInterfaceLock) {
947                 if (mRemoteDevice == null) {
948                     return; // Camera already closed
949                 }
950
951                 mInError = true;
952                 switch (errorCode) {
953                     case ERROR_CAMERA_DISCONNECTED:
954                         r = mCallOnDisconnected;
955                         break;
956                     default:
957                         Log.e(TAG, "Unknown error from camera device: " + errorCode);
958                         // no break
959                     case ERROR_CAMERA_DEVICE:
960                     case ERROR_CAMERA_SERVICE:
961                         r = new Runnable() {
962                             @Override
963                             public void run() {
964                                 if (!CameraDeviceImpl.this.isClosed()) {
965                                     mDeviceListener.onError(CameraDeviceImpl.this, errorCode);
966                                 }
967                             }
968                         };
969                         break;
970                 }
971                 CameraDeviceImpl.this.mDeviceHandler.post(r);
972
973                 // Fire onCaptureSequenceCompleted
974                 if (DEBUG) {
975                     Log.v(TAG, String.format("got error frame %d", resultExtras.getFrameNumber()));
976                 }
977                 mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/true);
978                 checkAndFireSequenceComplete();
979             }
980         }
981
982         @Override
983         public void onCameraIdle() {
984             if (DEBUG) {
985                 Log.d(TAG, "Camera now idle");
986             }
987             synchronized(mInterfaceLock) {
988                 if (mRemoteDevice == null) return; // Camera already closed
989
990                 if (!CameraDeviceImpl.this.mIdle) {
991                     CameraDeviceImpl.this.mDeviceHandler.post(mCallOnIdle);
992                 }
993                 CameraDeviceImpl.this.mIdle = true;
994             }
995         }
996
997         @Override
998         public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) {
999             int requestId = resultExtras.getRequestId();
1000             if (DEBUG) {
1001                 Log.d(TAG, "Capture started for id " + requestId);
1002             }
1003             final CaptureListenerHolder holder;
1004
1005             synchronized(mInterfaceLock) {
1006                 if (mRemoteDevice == null) return; // Camera already closed
1007
1008                 // Get the listener for this frame ID, if there is one
1009                 holder = CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
1010
1011                 if (holder == null) {
1012                     return;
1013                 }
1014
1015                 if (isClosed()) return;
1016
1017                 // Dispatch capture start notice
1018                 holder.getHandler().post(
1019                     new Runnable() {
1020                         @Override
1021                         public void run() {
1022                             if (!CameraDeviceImpl.this.isClosed()) {
1023                                 holder.getListener().onCaptureStarted(
1024                                     CameraDeviceImpl.this,
1025                                     holder.getRequest(resultExtras.getSubsequenceId()),
1026                                     timestamp);
1027                             }
1028                         }
1029                     });
1030
1031             }
1032         }
1033
1034         @Override
1035         public void onResultReceived(CameraMetadataNative result,
1036                 CaptureResultExtras resultExtras) throws RemoteException {
1037
1038             int requestId = resultExtras.getRequestId();
1039             long frameNumber = resultExtras.getFrameNumber();
1040
1041             if (DEBUG) {
1042                 Log.v(TAG, "Received result frame " + frameNumber + " for id "
1043                         + requestId);
1044             }
1045
1046             synchronized(mInterfaceLock) {
1047                 if (mRemoteDevice == null) return; // Camera already closed
1048
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));
1052
1053                 final CaptureListenerHolder holder =
1054                         CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
1055
1056                 boolean isPartialResult =
1057                         (resultExtras.getPartialResultCount() < mTotalPartialCount);
1058
1059                 // Update tracker (increment counter) when it's not a partial result.
1060                 if (!isPartialResult) {
1061                     mFrameNumberTracker.updateTracker(frameNumber,
1062                             /*error*/false);
1063                 }
1064
1065                 // Check if we have a listener for this
1066                 if (holder == null) {
1067                     if (DEBUG) {
1068                         Log.d(TAG,
1069                                 "holder is null, early return at frame "
1070                                         + frameNumber);
1071                     }
1072                     return;
1073                 }
1074
1075                 if (isClosed()) {
1076                     if (DEBUG) {
1077                         Log.d(TAG,
1078                                 "camera is closed, early return at frame "
1079                                         + frameNumber);
1080                     }
1081                     return;
1082                 }
1083
1084                 final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
1085
1086
1087                 Runnable resultDispatch = null;
1088
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);
1093
1094                     // Partial result
1095                     resultDispatch = new Runnable() {
1096                         @Override
1097                         public void run() {
1098                             if (!CameraDeviceImpl.this.isClosed()){
1099                                 holder.getListener().onCaptureProgressed(
1100                                     CameraDeviceImpl.this,
1101                                     request,
1102                                     resultAsCapture);
1103                             }
1104                         }
1105                     };
1106                 } else {
1107                     final TotalCaptureResult resultAsCapture =
1108                             new TotalCaptureResult(result, request, resultExtras);
1109
1110                     // Final capture result
1111                     resultDispatch = new Runnable() {
1112                         @Override
1113                         public void run() {
1114                             if (!CameraDeviceImpl.this.isClosed()){
1115                                 holder.getListener().onCaptureCompleted(
1116                                     CameraDeviceImpl.this,
1117                                     request,
1118                                     resultAsCapture);
1119                             }
1120                         }
1121                     };
1122                 }
1123
1124                 holder.getHandler().post(resultDispatch);
1125
1126                 // Fire onCaptureSequenceCompleted
1127                 if (!isPartialResult) {
1128                     checkAndFireSequenceComplete();
1129                 }
1130
1131             }
1132         }
1133
1134     }
1135
1136     /**
1137      * Default handler management.
1138      *
1139      * <p>
1140      * If handler is null, get the current thread's
1141      * Looper to create a Handler with. If no looper exists, throw {@code IllegalArgumentException}.
1142      * </p>
1143      */
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!");
1150             }
1151             handler = new Handler(looper);
1152         }
1153         return handler;
1154     }
1155
1156     /**
1157      * Default handler management, conditional on there being a listener.
1158      *
1159      * <p>If the listener isn't null, check the handler, otherwise pass it through.</p>
1160      */
1161     static <T> Handler checkHandler(Handler handler, T listener) {
1162         if (listener != null) {
1163             return checkHandler(handler);
1164         }
1165         return handler;
1166     }
1167
1168     private void checkIfCameraClosedOrInError() throws CameraAccessException {
1169         if (mInError) {
1170             throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
1171                     "The camera device has encountered a serious error");
1172         }
1173         if (mRemoteDevice == null) {
1174             throw new IllegalStateException("CameraDevice was already closed");
1175         }
1176     }
1177
1178     /** Whether the camera device has started to close (may not yet have finished) */
1179     private boolean isClosed() {
1180         return mClosing;
1181     }
1182
1183     private CameraCharacteristics getCharacteristics() {
1184         return mCharacteristics;
1185     }
1186 }