OSDN Git Service

Allow media projections to create public presentations.
[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         if (listener != null) {
547             handler = checkHandler(handler);
548         }
549
550         // Make sure that there all requests have at least 1 surface; all surfaces are non-null
551         for (CaptureRequest request : requestList) {
552             if (request.getTargets().isEmpty()) {
553                 throw new IllegalArgumentException(
554                         "Each request must have at least one Surface target");
555             }
556
557             for (Surface surface : request.getTargets()) {
558                 if (surface == null) {
559                     throw new IllegalArgumentException("Null Surface targets are not allowed");
560                 }
561             }
562         }
563
564         synchronized(mInterfaceLock) {
565             checkIfCameraClosedOrInError();
566             int requestId;
567
568             if (repeating) {
569                 stopRepeating();
570             }
571
572             LongParcelable lastFrameNumberRef = new LongParcelable();
573             try {
574                 requestId = mRemoteDevice.submitRequestList(requestList, repeating,
575                         /*out*/lastFrameNumberRef);
576                 if (DEBUG) {
577                     Log.v(TAG, "last frame number " + lastFrameNumberRef.getNumber());
578                 }
579             } catch (CameraRuntimeException e) {
580                 throw e.asChecked();
581             } catch (RemoteException e) {
582                 // impossible
583                 return -1;
584             }
585
586             if (listener != null) {
587                 mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener,
588                         requestList, handler, repeating));
589             } else {
590                 if (DEBUG) {
591                     Log.d(TAG, "Listen for request " + requestId + " is null");
592                 }
593             }
594
595             long lastFrameNumber = lastFrameNumberRef.getNumber();
596
597             if (repeating) {
598                 if (mRepeatingRequestId != REQUEST_ID_NONE) {
599                     checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
600                 }
601                 mRepeatingRequestId = requestId;
602             } else {
603                 mFrameNumberRequestPairs.add(
604                         new SimpleEntry<Long, Integer>(lastFrameNumber, requestId));
605             }
606
607             if (mIdle) {
608                 mDeviceHandler.post(mCallOnActive);
609             }
610             mIdle = false;
611
612             return requestId;
613         }
614     }
615
616     @Override
617     public int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
618             Handler handler) throws CameraAccessException {
619         List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
620         requestList.add(request);
621         return submitCaptureRequest(requestList, listener, handler, /*streaming*/true);
622     }
623
624     @Override
625     public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
626             Handler handler) throws CameraAccessException {
627         if (requests == null || requests.isEmpty()) {
628             throw new IllegalArgumentException("At least one request must be given");
629         }
630         return submitCaptureRequest(requests, listener, handler, /*streaming*/true);
631     }
632
633     @Override
634     public void stopRepeating() throws CameraAccessException {
635
636         synchronized(mInterfaceLock) {
637             checkIfCameraClosedOrInError();
638             if (mRepeatingRequestId != REQUEST_ID_NONE) {
639
640                 int requestId = mRepeatingRequestId;
641                 mRepeatingRequestId = REQUEST_ID_NONE;
642
643                 // Queue for deletion after in-flight requests finish
644                 if (mCaptureListenerMap.get(requestId) != null) {
645                     mRepeatingRequestIdDeletedList.add(requestId);
646                 }
647
648                 try {
649                     LongParcelable lastFrameNumberRef = new LongParcelable();
650                     mRemoteDevice.cancelRequest(requestId, /*out*/lastFrameNumberRef);
651                     long lastFrameNumber = lastFrameNumberRef.getNumber();
652
653                     checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber);
654
655                 } catch (CameraRuntimeException e) {
656                     throw e.asChecked();
657                 } catch (RemoteException e) {
658                     // impossible
659                     return;
660                 }
661             }
662         }
663     }
664
665     private void waitUntilIdle() throws CameraAccessException {
666
667         synchronized(mInterfaceLock) {
668             checkIfCameraClosedOrInError();
669
670             if (mRepeatingRequestId != REQUEST_ID_NONE) {
671                 throw new IllegalStateException("Active repeating request ongoing");
672             }
673             try {
674                 mRemoteDevice.waitUntilIdle();
675             } catch (CameraRuntimeException e) {
676                 throw e.asChecked();
677             } catch (RemoteException e) {
678                 // impossible
679                 return;
680             }
681         }
682     }
683
684     @Override
685     public void flush() throws CameraAccessException {
686         synchronized(mInterfaceLock) {
687             checkIfCameraClosedOrInError();
688
689             mDeviceHandler.post(mCallOnBusy);
690             try {
691                 LongParcelable lastFrameNumberRef = new LongParcelable();
692                 mRemoteDevice.flush(/*out*/lastFrameNumberRef);
693                 if (mRepeatingRequestId != REQUEST_ID_NONE) {
694                     long lastFrameNumber = lastFrameNumberRef.getNumber();
695                     checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
696                     mRepeatingRequestId = REQUEST_ID_NONE;
697                 }
698             } catch (CameraRuntimeException e) {
699                 throw e.asChecked();
700             } catch (RemoteException e) {
701                 // impossible
702                 return;
703             }
704         }
705     }
706
707     @Override
708     public void close() {
709         synchronized (mInterfaceLock) {
710             try {
711                 if (mRemoteDevice != null) {
712                     mRemoteDevice.disconnect();
713                 }
714             } catch (CameraRuntimeException e) {
715                 Log.e(TAG, "Exception while closing: ", e.asChecked());
716             } catch (RemoteException e) {
717                 // impossible
718             }
719
720             // Only want to fire the onClosed callback once;
721             // either a normal close where the remote device is valid
722             // or a close after a startup error (no remote device but in error state)
723             if (mRemoteDevice != null || mInError) {
724                 mDeviceHandler.post(mCallOnClosed);
725             }
726
727             mRemoteDevice = null;
728             mInError = false;
729         }
730     }
731
732     @Override
733     protected void finalize() throws Throwable {
734         try {
735             close();
736         }
737         finally {
738             super.finalize();
739         }
740     }
741
742     static class CaptureListenerHolder {
743
744         private final boolean mRepeating;
745         private final CaptureListener mListener;
746         private final List<CaptureRequest> mRequestList;
747         private final Handler mHandler;
748
749         CaptureListenerHolder(CaptureListener listener, List<CaptureRequest> requestList,
750                 Handler handler, boolean repeating) {
751             if (listener == null || handler == null) {
752                 throw new UnsupportedOperationException(
753                     "Must have a valid handler and a valid listener");
754             }
755             mRepeating = repeating;
756             mHandler = handler;
757             mRequestList = new ArrayList<CaptureRequest>(requestList);
758             mListener = listener;
759         }
760
761         public boolean isRepeating() {
762             return mRepeating;
763         }
764
765         public CaptureListener getListener() {
766             return mListener;
767         }
768
769         public CaptureRequest getRequest(int subsequenceId) {
770             if (subsequenceId >= mRequestList.size()) {
771                 throw new IllegalArgumentException(
772                         String.format(
773                                 "Requested subsequenceId %d is larger than request list size %d.",
774                                 subsequenceId, mRequestList.size()));
775             } else {
776                 if (subsequenceId < 0) {
777                     throw new IllegalArgumentException(String.format(
778                             "Requested subsequenceId %d is negative", subsequenceId));
779                 } else {
780                     return mRequestList.get(subsequenceId);
781                 }
782             }
783         }
784
785         public CaptureRequest getRequest() {
786             return getRequest(0);
787         }
788
789         public Handler getHandler() {
790             return mHandler;
791         }
792
793     }
794
795     /**
796      * This class tracks the last frame number for submitted requests.
797      */
798     public class FrameNumberTracker {
799
800         private long mCompletedFrameNumber = -1;
801         private final TreeSet<Long> mFutureErrorSet = new TreeSet<Long>();
802
803         private void update() {
804             Iterator<Long> iter = mFutureErrorSet.iterator();
805             while (iter.hasNext()) {
806                 long errorFrameNumber = iter.next();
807                 if (errorFrameNumber == mCompletedFrameNumber + 1) {
808                     mCompletedFrameNumber++;
809                     iter.remove();
810                 } else {
811                     break;
812                 }
813             }
814         }
815
816         /**
817          * This function is called every time when a result or an error is received.
818          * @param frameNumber: the frame number corresponding to the result or error
819          * @param isError: true if it is an error, false if it is not an error
820          */
821         public void updateTracker(long frameNumber, boolean isError) {
822             if (isError) {
823                 mFutureErrorSet.add(frameNumber);
824             } else {
825                 /**
826                  * HAL cannot send an OnResultReceived for frame N unless it knows for
827                  * sure that all frames prior to N have either errored out or completed.
828                  * So if the current frame is not an error, then all previous frames
829                  * should have arrived. The following line checks whether this holds.
830                  */
831                 if (frameNumber != mCompletedFrameNumber + 1) {
832                     Log.e(TAG, String.format(
833                             "result frame number %d comes out of order, should be %d + 1",
834                             frameNumber, mCompletedFrameNumber));
835                 }
836                 mCompletedFrameNumber++;
837             }
838             update();
839         }
840
841         public long getCompletedFrameNumber() {
842             return mCompletedFrameNumber;
843         }
844
845     }
846
847     private void checkAndFireSequenceComplete() {
848         long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
849         Iterator<SimpleEntry<Long, Integer> > iter = mFrameNumberRequestPairs.iterator();
850         while (iter.hasNext()) {
851             final SimpleEntry<Long, Integer> frameNumberRequestPair = iter.next();
852             if (frameNumberRequestPair.getKey() <= completedFrameNumber) {
853
854                 // remove request from mCaptureListenerMap
855                 final int requestId = frameNumberRequestPair.getValue();
856                 final CaptureListenerHolder holder;
857                 synchronized(mInterfaceLock) {
858                     if (mRemoteDevice == null) {
859                         Log.w(TAG, "Camera closed while checking sequences");
860                         return;
861                     }
862
863                     int index = mCaptureListenerMap.indexOfKey(requestId);
864                     holder = (index >= 0) ? mCaptureListenerMap.valueAt(index)
865                             : null;
866                     if (holder != null) {
867                         mCaptureListenerMap.removeAt(index);
868                         if (DEBUG) {
869                             Log.v(TAG, String.format(
870                                     "remove holder for requestId %d, "
871                                     + "because lastFrame %d is <= %d",
872                                     requestId, frameNumberRequestPair.getKey(),
873                                     completedFrameNumber));
874                         }
875                     }
876                 }
877                 iter.remove();
878
879                 // Call onCaptureSequenceCompleted
880                 if (holder != null) {
881                     Runnable resultDispatch = new Runnable() {
882                         @Override
883                         public void run() {
884                             if (!CameraDeviceImpl.this.isClosed()){
885                                 if (DEBUG) {
886                                     Log.d(TAG, String.format(
887                                             "fire sequence complete for request %d",
888                                             requestId));
889                                 }
890
891                                 long lastFrameNumber = frameNumberRequestPair.getKey();
892                                 if (lastFrameNumber < Integer.MIN_VALUE
893                                         || lastFrameNumber > Integer.MAX_VALUE) {
894                                     throw new AssertionError(lastFrameNumber
895                                             + " cannot be cast to int");
896                                 }
897                                 holder.getListener().onCaptureSequenceCompleted(
898                                     CameraDeviceImpl.this,
899                                     requestId,
900                                     lastFrameNumber);
901                             }
902                         }
903                     };
904                     holder.getHandler().post(resultDispatch);
905                 }
906
907             }
908         }
909     }
910
911     public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
912
913         //
914         // Constants below need to be kept up-to-date with
915         // frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h
916         //
917
918         //
919         // Error codes for onCameraError
920         //
921
922         /**
923          * Camera has been disconnected
924          */
925         static final int ERROR_CAMERA_DISCONNECTED = 0;
926
927         /**
928          * Camera has encountered a device-level error
929          * Matches CameraDevice.StateListener#ERROR_CAMERA_DEVICE
930          */
931         static final int ERROR_CAMERA_DEVICE = 1;
932
933         /**
934          * Camera has encountered a service-level error
935          * Matches CameraDevice.StateListener#ERROR_CAMERA_SERVICE
936          */
937         static final int ERROR_CAMERA_SERVICE = 2;
938
939         @Override
940         public IBinder asBinder() {
941             return this;
942         }
943
944         @Override
945         public void onCameraError(final int errorCode, CaptureResultExtras resultExtras) {
946             Runnable r = null;
947
948             synchronized(mInterfaceLock) {
949                 if (mRemoteDevice == null) {
950                     return; // Camera already closed
951                 }
952
953                 mInError = true;
954                 switch (errorCode) {
955                     case ERROR_CAMERA_DISCONNECTED:
956                         r = mCallOnDisconnected;
957                         break;
958                     default:
959                         Log.e(TAG, "Unknown error from camera device: " + errorCode);
960                         // no break
961                     case ERROR_CAMERA_DEVICE:
962                     case ERROR_CAMERA_SERVICE:
963                         r = new Runnable() {
964                             @Override
965                             public void run() {
966                                 if (!CameraDeviceImpl.this.isClosed()) {
967                                     mDeviceListener.onError(CameraDeviceImpl.this, errorCode);
968                                 }
969                             }
970                         };
971                         break;
972                 }
973                 CameraDeviceImpl.this.mDeviceHandler.post(r);
974
975                 // Fire onCaptureSequenceCompleted
976                 if (DEBUG) {
977                     Log.v(TAG, String.format("got error frame %d", resultExtras.getFrameNumber()));
978                 }
979                 mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/true);
980                 checkAndFireSequenceComplete();
981             }
982         }
983
984         @Override
985         public void onCameraIdle() {
986             if (DEBUG) {
987                 Log.d(TAG, "Camera now idle");
988             }
989             synchronized(mInterfaceLock) {
990                 if (mRemoteDevice == null) return; // Camera already closed
991
992                 if (!CameraDeviceImpl.this.mIdle) {
993                     CameraDeviceImpl.this.mDeviceHandler.post(mCallOnIdle);
994                 }
995                 CameraDeviceImpl.this.mIdle = true;
996             }
997         }
998
999         @Override
1000         public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) {
1001             int requestId = resultExtras.getRequestId();
1002             if (DEBUG) {
1003                 Log.d(TAG, "Capture started for id " + requestId);
1004             }
1005             final CaptureListenerHolder holder;
1006
1007             synchronized(mInterfaceLock) {
1008                 if (mRemoteDevice == null) return; // Camera already closed
1009
1010                 // Get the listener for this frame ID, if there is one
1011                 holder = CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
1012
1013                 if (holder == null) {
1014                     return;
1015                 }
1016
1017                 if (isClosed()) return;
1018
1019                 // Dispatch capture start notice
1020                 holder.getHandler().post(
1021                     new Runnable() {
1022                         @Override
1023                         public void run() {
1024                             if (!CameraDeviceImpl.this.isClosed()) {
1025                                 holder.getListener().onCaptureStarted(
1026                                     CameraDeviceImpl.this,
1027                                     holder.getRequest(resultExtras.getSubsequenceId()),
1028                                     timestamp);
1029                             }
1030                         }
1031                     });
1032
1033             }
1034         }
1035
1036         @Override
1037         public void onResultReceived(CameraMetadataNative result,
1038                 CaptureResultExtras resultExtras) throws RemoteException {
1039
1040             int requestId = resultExtras.getRequestId();
1041             long frameNumber = resultExtras.getFrameNumber();
1042
1043             if (DEBUG) {
1044                 Log.v(TAG, "Received result frame " + frameNumber + " for id "
1045                         + requestId);
1046             }
1047
1048             synchronized(mInterfaceLock) {
1049                 if (mRemoteDevice == null) return; // Camera already closed
1050
1051                 // TODO: Handle CameraCharacteristics access from CaptureResult correctly.
1052                 result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE,
1053                         getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE));
1054
1055                 final CaptureListenerHolder holder =
1056                         CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
1057
1058                 boolean isPartialResult =
1059                         (resultExtras.getPartialResultCount() < mTotalPartialCount);
1060
1061                 // Update tracker (increment counter) when it's not a partial result.
1062                 if (!isPartialResult) {
1063                     mFrameNumberTracker.updateTracker(frameNumber,
1064                             /*error*/false);
1065                 }
1066
1067                 // Check if we have a listener for this
1068                 if (holder == null) {
1069                     if (DEBUG) {
1070                         Log.d(TAG,
1071                                 "holder is null, early return at frame "
1072                                         + frameNumber);
1073                     }
1074                     return;
1075                 }
1076
1077                 if (isClosed()) {
1078                     if (DEBUG) {
1079                         Log.d(TAG,
1080                                 "camera is closed, early return at frame "
1081                                         + frameNumber);
1082                     }
1083                     return;
1084                 }
1085
1086                 final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
1087
1088
1089                 Runnable resultDispatch = null;
1090
1091                 // Either send a partial result or the final capture completed result
1092                 if (isPartialResult) {
1093                     final CaptureResult resultAsCapture =
1094                             new CaptureResult(result, request, resultExtras);
1095
1096                     // Partial result
1097                     resultDispatch = new Runnable() {
1098                         @Override
1099                         public void run() {
1100                             if (!CameraDeviceImpl.this.isClosed()){
1101                                 holder.getListener().onCaptureProgressed(
1102                                     CameraDeviceImpl.this,
1103                                     request,
1104                                     resultAsCapture);
1105                             }
1106                         }
1107                     };
1108                 } else {
1109                     final TotalCaptureResult resultAsCapture =
1110                             new TotalCaptureResult(result, request, resultExtras);
1111
1112                     // Final capture result
1113                     resultDispatch = new Runnable() {
1114                         @Override
1115                         public void run() {
1116                             if (!CameraDeviceImpl.this.isClosed()){
1117                                 holder.getListener().onCaptureCompleted(
1118                                     CameraDeviceImpl.this,
1119                                     request,
1120                                     resultAsCapture);
1121                             }
1122                         }
1123                     };
1124                 }
1125
1126                 holder.getHandler().post(resultDispatch);
1127
1128                 // Fire onCaptureSequenceCompleted
1129                 if (!isPartialResult) {
1130                     checkAndFireSequenceComplete();
1131                 }
1132
1133             }
1134         }
1135
1136     }
1137
1138     /**
1139      * Default handler management.
1140      *
1141      * <p>
1142      * If handler is null, get the current thread's
1143      * Looper to create a Handler with. If no looper exists, throw {@code IllegalArgumentException}.
1144      * </p>
1145      */
1146     static Handler checkHandler(Handler handler) {
1147         if (handler == null) {
1148             Looper looper = Looper.myLooper();
1149             if (looper == null) {
1150                 throw new IllegalArgumentException(
1151                     "No handler given, and current thread has no looper!");
1152             }
1153             handler = new Handler(looper);
1154         }
1155         return handler;
1156     }
1157
1158     private void checkIfCameraClosedOrInError() throws CameraAccessException {
1159         if (mInError) {
1160             throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
1161                     "The camera device has encountered a serious error");
1162         }
1163         if (mRemoteDevice == null) {
1164             throw new IllegalStateException("CameraDevice was already closed");
1165         }
1166     }
1167
1168     /** Whether the camera device has started to close (may not yet have finished) */
1169     private boolean isClosed() {
1170         return mClosing;
1171     }
1172
1173     private CameraCharacteristics getCharacteristics() {
1174         return mCharacteristics;
1175     }
1176 }