OSDN Git Service

f5666bf337698d6190f302bfac1bf427e30a5e20
[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.CameraDevice;
25 import android.hardware.camera2.CaptureRequest;
26 import android.hardware.camera2.CaptureResult;
27 import android.hardware.camera2.CaptureFailure;
28 import android.hardware.camera2.ICameraDeviceCallbacks;
29 import android.hardware.camera2.ICameraDeviceUser;
30 import android.hardware.camera2.TotalCaptureResult;
31 import android.hardware.camera2.utils.CameraBinderDecorator;
32 import android.hardware.camera2.utils.CameraRuntimeException;
33 import android.hardware.camera2.utils.LongParcelable;
34 import android.os.Handler;
35 import android.os.IBinder;
36 import android.os.Looper;
37 import android.os.RemoteException;
38 import android.util.Log;
39 import android.util.SparseArray;
40 import android.view.Surface;
41
42 import java.util.AbstractMap.SimpleEntry;
43 import java.util.ArrayList;
44 import java.util.HashMap;
45 import java.util.HashSet;
46 import java.util.Iterator;
47 import java.util.List;
48 import java.util.TreeSet;
49
50 /**
51  * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
52  */
53 public class CameraDeviceImpl extends CameraDevice {
54
55     private final String TAG;
56     private final boolean DEBUG;
57
58     private static final int REQUEST_ID_NONE = -1;
59
60     // TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
61     private ICameraDeviceUser mRemoteDevice;
62
63     // Lock to synchronize cross-thread access to device public interface
64     private final Object mInterfaceLock = new Object();
65     private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
66
67     private final StateListener mDeviceListener;
68     private volatile StateListenerKK mSessionStateListener;
69     private final Handler mDeviceHandler;
70
71     private volatile boolean mClosing = false;
72     private boolean mInError = false;
73     private boolean mIdle = true;
74
75     /** map request IDs to listener/request data */
76     private final SparseArray<CaptureListenerHolder> mCaptureListenerMap =
77             new SparseArray<CaptureListenerHolder>();
78
79     private int mRepeatingRequestId = REQUEST_ID_NONE;
80     private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>();
81     // Map stream IDs to Surfaces
82     private final SparseArray<Surface> mConfiguredOutputs = new SparseArray<Surface>();
83
84     private final String mCameraId;
85     private final CameraCharacteristics mCharacteristics;
86     private final int mTotalPartialCount;
87
88     /**
89      * A list tracking request and its expected last frame.
90      * Updated when calling ICameraDeviceUser methods.
91      */
92     private final List<SimpleEntry</*frameNumber*/Long, /*requestId*/Integer>>
93             mFrameNumberRequestPairs = new ArrayList<SimpleEntry<Long, Integer>>();
94
95     /**
96      * An object tracking received frame numbers.
97      * Updated when receiving callbacks from ICameraDeviceCallbacks.
98      */
99     private final FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker();
100
101     private CameraCaptureSessionImpl mCurrentSession;
102
103     // Runnables for all state transitions, except error, which needs the
104     // error code argument
105
106     private final Runnable mCallOnOpened = new Runnable() {
107         @Override
108         public void run() {
109             StateListenerKK sessionListener = null;
110             synchronized(mInterfaceLock) {
111                 if (mRemoteDevice == null) return; // Camera already closed
112
113                 sessionListener = mSessionStateListener;
114             }
115             if (sessionListener != null) {
116                 sessionListener.onOpened(CameraDeviceImpl.this);
117             }
118             mDeviceListener.onOpened(CameraDeviceImpl.this);
119         }
120     };
121
122     private final Runnable mCallOnUnconfigured = new Runnable() {
123         @Override
124         public void run() {
125             StateListenerKK sessionListener = null;
126             synchronized(mInterfaceLock) {
127                 if (mRemoteDevice == null) return; // Camera already closed
128
129                 sessionListener = mSessionStateListener;
130             }
131             if (sessionListener != null) {
132                 sessionListener.onUnconfigured(CameraDeviceImpl.this);
133             }
134         }
135     };
136
137     private final Runnable mCallOnActive = new Runnable() {
138         @Override
139         public void run() {
140             StateListenerKK sessionListener = null;
141             synchronized(mInterfaceLock) {
142                 if (mRemoteDevice == null) return; // Camera already closed
143
144                 sessionListener = mSessionStateListener;
145             }
146             if (sessionListener != null) {
147                 sessionListener.onActive(CameraDeviceImpl.this);
148             }
149         }
150     };
151
152     private final Runnable mCallOnBusy = new Runnable() {
153         @Override
154         public void run() {
155             StateListenerKK sessionListener = null;
156             synchronized(mInterfaceLock) {
157                 if (mRemoteDevice == null) return; // Camera already closed
158
159                 sessionListener = mSessionStateListener;
160             }
161             if (sessionListener != null) {
162                 sessionListener.onBusy(CameraDeviceImpl.this);
163             }
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             StateListenerKK 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             StateListenerKK 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         }
200     };
201
202     private final Runnable mCallOnDisconnected = new Runnable() {
203         @Override
204         public void run() {
205             StateListenerKK sessionListener = null;
206             synchronized(mInterfaceLock) {
207                 if (mRemoteDevice == null) return; // Camera already closed
208
209                 sessionListener = mSessionStateListener;
210             }
211             if (sessionListener != null) {
212                 sessionListener.onDisconnected(CameraDeviceImpl.this);
213             }
214             mDeviceListener.onDisconnected(CameraDeviceImpl.this);
215         }
216     };
217
218     public CameraDeviceImpl(String cameraId, StateListener listener, Handler handler,
219                         CameraCharacteristics characteristics) {
220         if (cameraId == null || listener == null || handler == null || characteristics == null) {
221             throw new IllegalArgumentException("Null argument given");
222         }
223         mCameraId = cameraId;
224         mDeviceListener = listener;
225         mDeviceHandler = handler;
226         mCharacteristics = characteristics;
227
228         final int MAX_TAG_LEN = 23;
229         String tag = String.format("CameraDevice-JV-%s", mCameraId);
230         if (tag.length() > MAX_TAG_LEN) {
231             tag = tag.substring(0, MAX_TAG_LEN);
232         }
233         TAG = tag;
234         DEBUG = Log.isLoggable(TAG, Log.DEBUG);
235
236         Integer partialCount =
237                 mCharacteristics.get(CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT);
238         if (partialCount == null) {
239             // 1 means partial result is not supported.
240             mTotalPartialCount = 1;
241         } else {
242             mTotalPartialCount = partialCount;
243         }
244     }
245
246     public CameraDeviceCallbacks getCallbacks() {
247         return mCallbacks;
248     }
249
250     public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
251         synchronized(mInterfaceLock) {
252             // TODO: Move from decorator to direct binder-mediated exceptions
253             // If setRemoteFailure already called, do nothing
254             if (mInError) return;
255
256             mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice);
257
258             mDeviceHandler.post(mCallOnOpened);
259             mDeviceHandler.post(mCallOnUnconfigured);
260         }
261     }
262
263     /**
264      * Call to indicate failed connection to a remote camera device.
265      *
266      * <p>This places the camera device in the error state and informs the listener.
267      * Use in place of setRemoteDevice() when startup fails.</p>
268      */
269     public void setRemoteFailure(final CameraRuntimeException failure) {
270         int failureCode = StateListener.ERROR_CAMERA_DEVICE;
271         boolean failureIsError = true;
272
273         switch (failure.getReason()) {
274             case CameraAccessException.CAMERA_IN_USE:
275                 failureCode = StateListener.ERROR_CAMERA_IN_USE;
276                 break;
277             case CameraAccessException.MAX_CAMERAS_IN_USE:
278                 failureCode = StateListener.ERROR_MAX_CAMERAS_IN_USE;
279                 break;
280             case CameraAccessException.CAMERA_DISABLED:
281                 failureCode = StateListener.ERROR_CAMERA_DISABLED;
282                 break;
283             case CameraAccessException.CAMERA_DISCONNECTED:
284                 failureIsError = false;
285                 break;
286             case CameraAccessException.CAMERA_ERROR:
287                 failureCode = StateListener.ERROR_CAMERA_DEVICE;
288                 break;
289             default:
290                 Log.wtf(TAG, "Unknown failure in opening camera device: " + failure.getReason());
291                 break;
292         }
293         final int code = failureCode;
294         final boolean isError = failureIsError;
295         synchronized(mInterfaceLock) {
296             mInError = true;
297             mDeviceHandler.post(new Runnable() {
298                 @Override
299                 public void run() {
300                     if (isError) {
301                         mDeviceListener.onError(CameraDeviceImpl.this, code);
302                     } else {
303                         mDeviceListener.onDisconnected(CameraDeviceImpl.this);
304                     }
305                 }
306             });
307         }
308     }
309
310     @Override
311     public String getId() {
312         return mCameraId;
313     }
314
315     public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
316         // Leave this here for backwards compatibility with older code using this directly
317         configureOutputsChecked(outputs);
318     }
319
320     /**
321      * Attempt to configure the outputs; the device goes to idle and then configures the
322      * new outputs if possible.
323      *
324      * <p>The configuration may gracefully fail, if there are too many outputs, if the formats
325      * are not supported, or if the sizes for that format is not supported. In this case this
326      * function will return {@code false} and the unconfigured callback will be fired.</p>
327      *
328      * <p>If the configuration succeeds (with 1 or more outputs), then the idle callback is fired.
329      * Unconfiguring the device always fires the idle callback.</p>
330      *
331      * @param outputs a list of one or more surfaces, or {@code null} to unconfigure
332      * @return whether or not the configuration was successful
333      *
334      * @throws CameraAccessException if there were any unexpected problems during configuration
335      */
336     public boolean configureOutputsChecked(List<Surface> outputs) throws CameraAccessException {
337         // Treat a null input the same an empty list
338         if (outputs == null) {
339             outputs = new ArrayList<Surface>();
340         }
341         boolean success = false;
342
343         synchronized(mInterfaceLock) {
344             checkIfCameraClosedOrInError();
345
346             HashSet<Surface> addSet = new HashSet<Surface>(outputs);    // Streams to create
347             List<Integer> deleteList = new ArrayList<Integer>();        // Streams to delete
348
349             // Determine which streams need to be created, which to be deleted
350             for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
351                 int streamId = mConfiguredOutputs.keyAt(i);
352                 Surface s = mConfiguredOutputs.valueAt(i);
353
354                 if (!outputs.contains(s)) {
355                     deleteList.add(streamId);
356                 } else {
357                     addSet.remove(s);  // Don't create a stream previously created
358                 }
359             }
360
361             mDeviceHandler.post(mCallOnBusy);
362             stopRepeating();
363
364             try {
365                 waitUntilIdle();
366
367                 mRemoteDevice.beginConfigure();
368                 // Delete all streams first (to free up HW resources)
369                 for (Integer streamId : deleteList) {
370                     mRemoteDevice.deleteStream(streamId);
371                     mConfiguredOutputs.delete(streamId);
372                 }
373
374                 // Add all new streams
375                 for (Surface s : addSet) {
376                     // TODO: remove width,height,format since we are ignoring
377                     // it.
378                     int streamId = mRemoteDevice.createStream(0, 0, 0, s);
379                     mConfiguredOutputs.put(streamId, s);
380                 }
381
382                 try {
383                     mRemoteDevice.endConfigure();
384                 }
385                 catch (IllegalArgumentException e) {
386                     // OK. camera service can reject stream config if it's not supported by HAL
387                     // This is only the result of a programmer misusing the camera2 api.
388                     Log.w(TAG, "Stream configuration failed");
389                     return false;
390                 }
391
392                 success = true;
393             } catch (CameraRuntimeException e) {
394                 if (e.getReason() == CAMERA_IN_USE) {
395                     throw new IllegalStateException("The camera is currently busy." +
396                             " You must wait until the previous operation completes.");
397                 }
398
399                 throw e.asChecked();
400             } catch (RemoteException e) {
401                 // impossible
402                 return false;
403             } finally {
404                 if (success && outputs.size() > 0) {
405                     mDeviceHandler.post(mCallOnIdle);
406                 } else {
407                     // Always return to the 'unconfigured' state if we didn't hit a fatal error
408                     mDeviceHandler.post(mCallOnUnconfigured);
409                 }
410             }
411         }
412
413         return success;
414     }
415
416     @Override
417     public void createCaptureSession(List<Surface> outputs,
418             CameraCaptureSession.StateListener listener, Handler handler)
419             throws CameraAccessException {
420         synchronized(mInterfaceLock) {
421             if (DEBUG) {
422                 Log.d(TAG, "createCaptureSession");
423             }
424
425             checkIfCameraClosedOrInError();
426
427             // Notify current session that it's going away, before starting camera operations
428             // After this call completes, the session is not allowed to call into CameraDeviceImpl
429             if (mCurrentSession != null) {
430                 mCurrentSession.replaceSessionClose();
431             }
432
433             // TODO: dont block for this
434             boolean configureSuccess = true;
435             CameraAccessException pendingException = null;
436             try {
437                 configureSuccess = configureOutputsChecked(outputs); // and then block until IDLE
438             } catch (CameraAccessException e) {
439                 configureSuccess = false;
440                 pendingException = e;
441                 if (DEBUG) {
442                     Log.v(TAG, "createCaptureSession - failed with exception ", e);
443                 }
444             }
445
446             // Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise.
447             CameraCaptureSessionImpl newSession =
448                     new CameraCaptureSessionImpl(outputs, listener, handler, this, mDeviceHandler,
449                             configureSuccess);
450
451             // TODO: wait until current session closes, then create the new session
452             mCurrentSession = newSession;
453
454             if (pendingException != null) {
455                 throw pendingException;
456             }
457
458             mSessionStateListener = mCurrentSession.getDeviceStateListener();
459         }
460     }
461
462     /**
463      * For use by backwards-compatibility code only.
464      */
465     public void setSessionListener(StateListenerKK sessionListener) {
466         synchronized(mInterfaceLock) {
467             mSessionStateListener = sessionListener;
468         }
469     }
470
471     @Override
472     public CaptureRequest.Builder createCaptureRequest(int templateType)
473             throws CameraAccessException {
474         synchronized(mInterfaceLock) {
475             checkIfCameraClosedOrInError();
476
477             CameraMetadataNative templatedRequest = new CameraMetadataNative();
478
479             try {
480                 mRemoteDevice.createDefaultRequest(templateType, /*out*/templatedRequest);
481             } catch (CameraRuntimeException e) {
482                 throw e.asChecked();
483             } catch (RemoteException e) {
484                 // impossible
485                 return null;
486             }
487
488             CaptureRequest.Builder builder =
489                     new CaptureRequest.Builder(templatedRequest);
490
491             return builder;
492         }
493     }
494
495     public int capture(CaptureRequest request, CaptureListener listener, Handler handler)
496             throws CameraAccessException {
497         if (DEBUG) {
498             Log.d(TAG, "calling capture");
499         }
500         List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
501         requestList.add(request);
502         return submitCaptureRequest(requestList, listener, handler, /*streaming*/false);
503     }
504
505     public int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
506             Handler handler) throws CameraAccessException {
507         if (requests == null || requests.isEmpty()) {
508             throw new IllegalArgumentException("At least one request must be given");
509         }
510         return submitCaptureRequest(requests, listener, handler, /*streaming*/false);
511     }
512
513     /**
514      * This method checks lastFrameNumber returned from ICameraDeviceUser methods for
515      * starting and stopping repeating request and flushing.
516      *
517      * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never
518      * sent to HAL. Then onCaptureSequenceAborted is immediately triggered.
519      * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber pair
520      * is added to the list mFrameNumberRequestPairs.</p>
521      *
522      * @param requestId the request ID of the current repeating request.
523      *
524      * @param lastFrameNumber last frame number returned from binder.
525      */
526     private void checkEarlyTriggerSequenceComplete(
527             final int requestId, final long lastFrameNumber) {
528         // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request
529         // was never sent to HAL. Should trigger onCaptureSequenceAborted immediately.
530         if (lastFrameNumber == CaptureListener.NO_FRAMES_CAPTURED) {
531             final CaptureListenerHolder holder;
532             int index = mCaptureListenerMap.indexOfKey(requestId);
533             holder = (index >= 0) ? mCaptureListenerMap.valueAt(index) : null;
534             if (holder != null) {
535                 mCaptureListenerMap.removeAt(index);
536                 if (DEBUG) {
537                     Log.v(TAG, String.format(
538                             "remove holder for requestId %d, "
539                             + "because lastFrame is %d.",
540                             requestId, lastFrameNumber));
541                 }
542             }
543
544             if (holder != null) {
545                 if (DEBUG) {
546                     Log.v(TAG, "immediately trigger onCaptureSequenceAborted because"
547                             + " request did not reach HAL");
548                 }
549
550                 Runnable resultDispatch = new Runnable() {
551                     @Override
552                     public void run() {
553                         if (!CameraDeviceImpl.this.isClosed()) {
554                             if (DEBUG) {
555                                 Log.d(TAG, String.format(
556                                         "early trigger sequence complete for request %d",
557                                         requestId));
558                             }
559                             if (lastFrameNumber < Integer.MIN_VALUE
560                                     || lastFrameNumber > Integer.MAX_VALUE) {
561                                 throw new AssertionError(lastFrameNumber + " cannot be cast to int");
562                             }
563                             holder.getListener().onCaptureSequenceAborted(
564                                     CameraDeviceImpl.this,
565                                     requestId);
566                         }
567                     }
568                 };
569                 holder.getHandler().post(resultDispatch);
570             } else {
571                 Log.w(TAG, String.format(
572                         "did not register listener to request %d",
573                         requestId));
574             }
575         } else {
576             mFrameNumberRequestPairs.add(
577                     new SimpleEntry<Long, Integer>(lastFrameNumber,
578                             requestId));
579         }
580     }
581
582     private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureListener listener,
583             Handler handler, boolean repeating) throws CameraAccessException {
584
585         // Need a valid handler, or current thread needs to have a looper, if
586         // listener is valid
587         handler = checkHandler(handler, listener);
588
589         // Make sure that there all requests have at least 1 surface; all surfaces are non-null
590         for (CaptureRequest request : requestList) {
591             if (request.getTargets().isEmpty()) {
592                 throw new IllegalArgumentException(
593                         "Each request must have at least one Surface target");
594             }
595
596             for (Surface surface : request.getTargets()) {
597                 if (surface == null) {
598                     throw new IllegalArgumentException("Null Surface targets are not allowed");
599                 }
600             }
601         }
602
603         synchronized(mInterfaceLock) {
604             checkIfCameraClosedOrInError();
605             int requestId;
606
607             if (repeating) {
608                 stopRepeating();
609             }
610
611             LongParcelable lastFrameNumberRef = new LongParcelable();
612             try {
613                 requestId = mRemoteDevice.submitRequestList(requestList, repeating,
614                         /*out*/lastFrameNumberRef);
615                 if (DEBUG) {
616                     Log.v(TAG, "last frame number " + lastFrameNumberRef.getNumber());
617                 }
618             } catch (CameraRuntimeException e) {
619                 throw e.asChecked();
620             } catch (RemoteException e) {
621                 // impossible
622                 return -1;
623             }
624
625             if (listener != null) {
626                 mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener,
627                         requestList, handler, repeating));
628             } else {
629                 if (DEBUG) {
630                     Log.d(TAG, "Listen for request " + requestId + " is null");
631                 }
632             }
633
634             long lastFrameNumber = lastFrameNumberRef.getNumber();
635
636             if (repeating) {
637                 if (mRepeatingRequestId != REQUEST_ID_NONE) {
638                     checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
639                 }
640                 mRepeatingRequestId = requestId;
641             } else {
642                 mFrameNumberRequestPairs.add(
643                         new SimpleEntry<Long, Integer>(lastFrameNumber, requestId));
644             }
645
646             if (mIdle) {
647                 mDeviceHandler.post(mCallOnActive);
648             }
649             mIdle = false;
650
651             return requestId;
652         }
653     }
654
655     public int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
656             Handler handler) throws CameraAccessException {
657         List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
658         requestList.add(request);
659         return submitCaptureRequest(requestList, listener, handler, /*streaming*/true);
660     }
661
662     public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
663             Handler handler) throws CameraAccessException {
664         if (requests == null || requests.isEmpty()) {
665             throw new IllegalArgumentException("At least one request must be given");
666         }
667         return submitCaptureRequest(requests, listener, handler, /*streaming*/true);
668     }
669
670     public void stopRepeating() throws CameraAccessException {
671
672         synchronized(mInterfaceLock) {
673             checkIfCameraClosedOrInError();
674             if (mRepeatingRequestId != REQUEST_ID_NONE) {
675
676                 int requestId = mRepeatingRequestId;
677                 mRepeatingRequestId = REQUEST_ID_NONE;
678
679                 // Queue for deletion after in-flight requests finish
680                 if (mCaptureListenerMap.get(requestId) != null) {
681                     mRepeatingRequestIdDeletedList.add(requestId);
682                 }
683
684                 try {
685                     LongParcelable lastFrameNumberRef = new LongParcelable();
686                     mRemoteDevice.cancelRequest(requestId, /*out*/lastFrameNumberRef);
687                     long lastFrameNumber = lastFrameNumberRef.getNumber();
688
689                     checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber);
690
691                 } catch (CameraRuntimeException e) {
692                     throw e.asChecked();
693                 } catch (RemoteException e) {
694                     // impossible
695                     return;
696                 }
697             }
698         }
699     }
700
701     private void waitUntilIdle() throws CameraAccessException {
702
703         synchronized(mInterfaceLock) {
704             checkIfCameraClosedOrInError();
705
706             if (mRepeatingRequestId != REQUEST_ID_NONE) {
707                 throw new IllegalStateException("Active repeating request ongoing");
708             }
709             try {
710                 mRemoteDevice.waitUntilIdle();
711             } catch (CameraRuntimeException e) {
712                 throw e.asChecked();
713             } catch (RemoteException e) {
714                 // impossible
715                 return;
716             }
717         }
718     }
719
720     public void flush() throws CameraAccessException {
721         synchronized(mInterfaceLock) {
722             checkIfCameraClosedOrInError();
723
724             mDeviceHandler.post(mCallOnBusy);
725
726             // If already idle, just do a busy->idle transition immediately, don't actually
727             // flush.
728             if (mIdle) {
729                 mDeviceHandler.post(mCallOnIdle);
730                 return;
731             }
732             try {
733                 LongParcelable lastFrameNumberRef = new LongParcelable();
734                 mRemoteDevice.flush(/*out*/lastFrameNumberRef);
735                 if (mRepeatingRequestId != REQUEST_ID_NONE) {
736                     long lastFrameNumber = lastFrameNumberRef.getNumber();
737                     checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
738                     mRepeatingRequestId = REQUEST_ID_NONE;
739                 }
740             } catch (CameraRuntimeException e) {
741                 throw e.asChecked();
742             } catch (RemoteException e) {
743                 // impossible
744                 return;
745             }
746         }
747     }
748
749     @Override
750     public void close() {
751         synchronized (mInterfaceLock) {
752             try {
753                 if (mRemoteDevice != null) {
754                     mRemoteDevice.disconnect();
755                 }
756             } catch (CameraRuntimeException e) {
757                 Log.e(TAG, "Exception while closing: ", e.asChecked());
758             } catch (RemoteException e) {
759                 // impossible
760             }
761
762             // Only want to fire the onClosed callback once;
763             // either a normal close where the remote device is valid
764             // or a close after a startup error (no remote device but in error state)
765             if (mRemoteDevice != null || mInError) {
766                 mDeviceHandler.post(mCallOnClosed);
767             }
768
769             mRemoteDevice = null;
770             mInError = false;
771         }
772     }
773
774     @Override
775     protected void finalize() throws Throwable {
776         try {
777             close();
778         }
779         finally {
780             super.finalize();
781         }
782     }
783
784     /**
785      * <p>A listener for tracking the progress of a {@link CaptureRequest}
786      * submitted to the camera device.</p>
787      *
788      */
789     public static abstract class CaptureListener {
790
791         /**
792          * This constant is used to indicate that no images were captured for
793          * the request.
794          *
795          * @hide
796          */
797         public static final int NO_FRAMES_CAPTURED = -1;
798
799         /**
800          * This method is called when the camera device has started capturing
801          * the output image for the request, at the beginning of image exposure.
802          *
803          * @see android.media.MediaActionSound
804          */
805         public void onCaptureStarted(CameraDevice camera,
806                 CaptureRequest request, long timestamp) {
807             // default empty implementation
808         }
809
810         /**
811          * This method is called when some results from an image capture are
812          * available.
813          *
814          * @hide
815          */
816         public void onCapturePartial(CameraDevice camera,
817                 CaptureRequest request, CaptureResult result) {
818             // default empty implementation
819         }
820
821         /**
822          * This method is called when an image capture makes partial forward progress; some
823          * (but not all) results from an image capture are available.
824          *
825          */
826         public void onCaptureProgressed(CameraDevice camera,
827                 CaptureRequest request, CaptureResult partialResult) {
828             // default empty implementation
829         }
830
831         /**
832          * This method is called when an image capture has fully completed and all the
833          * result metadata is available.
834          */
835         public void onCaptureCompleted(CameraDevice camera,
836                 CaptureRequest request, TotalCaptureResult result) {
837             // default empty implementation
838         }
839
840         /**
841          * This method is called instead of {@link #onCaptureCompleted} when the
842          * camera device failed to produce a {@link CaptureResult} for the
843          * request.
844          */
845         public void onCaptureFailed(CameraDevice camera,
846                 CaptureRequest request, CaptureFailure failure) {
847             // default empty implementation
848         }
849
850         /**
851          * This method is called independently of the others in CaptureListener,
852          * when a capture sequence finishes and all {@link CaptureResult}
853          * or {@link CaptureFailure} for it have been returned via this listener.
854          */
855         public void onCaptureSequenceCompleted(CameraDevice camera,
856                 int sequenceId, long frameNumber) {
857             // default empty implementation
858         }
859
860         /**
861          * This method is called independently of the others in CaptureListener,
862          * when a capture sequence aborts before any {@link CaptureResult}
863          * or {@link CaptureFailure} for it have been returned via this listener.
864          */
865         public void onCaptureSequenceAborted(CameraDevice camera,
866                 int sequenceId) {
867             // default empty implementation
868         }
869     }
870
871     /**
872      * A listener for notifications about the state of a camera device, adding in the callbacks that
873      * were part of the earlier KK API design, but now only used internally.
874      */
875     public static abstract class StateListenerKK extends StateListener {
876         /**
877          * The method called when a camera device has no outputs configured.
878          *
879          */
880         public void onUnconfigured(CameraDevice camera) {
881             // Default empty implementation
882         }
883
884         /**
885          * The method called when a camera device begins processing
886          * {@link CaptureRequest capture requests}.
887          *
888          */
889         public void onActive(CameraDevice camera) {
890             // Default empty implementation
891         }
892
893         /**
894          * The method called when a camera device is busy.
895          *
896          */
897         public void onBusy(CameraDevice camera) {
898             // Default empty implementation
899         }
900
901         /**
902          * The method called when a camera device has finished processing all
903          * submitted capture requests and has reached an idle state.
904          *
905          */
906         public void onIdle(CameraDevice camera) {
907             // Default empty implementation
908         }
909     }
910
911     static class CaptureListenerHolder {
912
913         private final boolean mRepeating;
914         private final CaptureListener mListener;
915         private final List<CaptureRequest> mRequestList;
916         private final Handler mHandler;
917
918         CaptureListenerHolder(CaptureListener listener, List<CaptureRequest> requestList,
919                 Handler handler, boolean repeating) {
920             if (listener == null || handler == null) {
921                 throw new UnsupportedOperationException(
922                     "Must have a valid handler and a valid listener");
923             }
924             mRepeating = repeating;
925             mHandler = handler;
926             mRequestList = new ArrayList<CaptureRequest>(requestList);
927             mListener = listener;
928         }
929
930         public boolean isRepeating() {
931             return mRepeating;
932         }
933
934         public CaptureListener getListener() {
935             return mListener;
936         }
937
938         public CaptureRequest getRequest(int subsequenceId) {
939             if (subsequenceId >= mRequestList.size()) {
940                 throw new IllegalArgumentException(
941                         String.format(
942                                 "Requested subsequenceId %d is larger than request list size %d.",
943                                 subsequenceId, mRequestList.size()));
944             } else {
945                 if (subsequenceId < 0) {
946                     throw new IllegalArgumentException(String.format(
947                             "Requested subsequenceId %d is negative", subsequenceId));
948                 } else {
949                     return mRequestList.get(subsequenceId);
950                 }
951             }
952         }
953
954         public CaptureRequest getRequest() {
955             return getRequest(0);
956         }
957
958         public Handler getHandler() {
959             return mHandler;
960         }
961
962     }
963
964     /**
965      * This class tracks the last frame number for submitted requests.
966      */
967     public class FrameNumberTracker {
968
969         private long mCompletedFrameNumber = -1;
970         private final TreeSet<Long> mFutureErrorSet = new TreeSet<Long>();
971         /** Map frame numbers to list of partial results */
972         private final HashMap<Long, List<CaptureResult>> mPartialResults = new HashMap<>();
973
974         private void update() {
975             Iterator<Long> iter = mFutureErrorSet.iterator();
976             while (iter.hasNext()) {
977                 long errorFrameNumber = iter.next();
978                 if (errorFrameNumber == mCompletedFrameNumber + 1) {
979                     mCompletedFrameNumber++;
980                     iter.remove();
981                 } else {
982                     break;
983                 }
984             }
985         }
986
987         /**
988          * This function is called every time when a result or an error is received.
989          * @param frameNumber the frame number corresponding to the result or error
990          * @param isError true if it is an error, false if it is not an error
991          */
992         public void updateTracker(long frameNumber, boolean isError) {
993             if (isError) {
994                 mFutureErrorSet.add(frameNumber);
995             } else {
996                 /**
997                  * HAL cannot send an OnResultReceived for frame N unless it knows for
998                  * sure that all frames prior to N have either errored out or completed.
999                  * So if the current frame is not an error, then all previous frames
1000                  * should have arrived. The following line checks whether this holds.
1001                  */
1002                 if (frameNumber != mCompletedFrameNumber + 1) {
1003                     Log.e(TAG, String.format(
1004                             "result frame number %d comes out of order, should be %d + 1",
1005                             frameNumber, mCompletedFrameNumber));
1006                 }
1007                 mCompletedFrameNumber++;
1008             }
1009             update();
1010         }
1011
1012         /**
1013          * This function is called every time a result has been completed.
1014          *
1015          * <p>It keeps a track of all the partial results already created for a particular
1016          * frame number.</p>
1017          *
1018          * @param frameNumber the frame number corresponding to the result
1019          * @param result the total or partial result
1020          * @param partial {@true} if the result is partial, {@code false} if total
1021          */
1022         public void updateTracker(long frameNumber, CaptureResult result, boolean partial) {
1023
1024             if (!partial) {
1025                 // Update the total result's frame status as being successful
1026                 updateTracker(frameNumber, /*isError*/false);
1027                 // Don't keep a list of total results, we don't need to track them
1028                 return;
1029             }
1030
1031             if (result == null) {
1032                 // Do not record blank results; this also means there will be no total result
1033                 // so it doesn't matter that the partials were not recorded
1034                 return;
1035             }
1036
1037             // Partial results must be aggregated in-order for that frame number
1038             List<CaptureResult> partials = mPartialResults.get(frameNumber);
1039             if (partials == null) {
1040                 partials = new ArrayList<>();
1041                 mPartialResults.put(frameNumber, partials);
1042             }
1043
1044             partials.add(result);
1045         }
1046
1047         /**
1048          * Attempt to pop off all of the partial results seen so far for the {@code frameNumber}.
1049          *
1050          * <p>Once popped-off, the partial results are forgotten (unless {@code updateTracker}
1051          * is called again with new partials for that frame number).</p>
1052          *
1053          * @param frameNumber the frame number corresponding to the result
1054          * @return a list of partial results for that frame with at least 1 element,
1055          *         or {@code null} if there were no partials recorded for that frame
1056          */
1057         public List<CaptureResult> popPartialResults(long frameNumber) {
1058             return mPartialResults.remove(frameNumber);
1059         }
1060
1061         public long getCompletedFrameNumber() {
1062             return mCompletedFrameNumber;
1063         }
1064
1065     }
1066
1067     private void checkAndFireSequenceComplete() {
1068         long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
1069         Iterator<SimpleEntry<Long, Integer> > iter = mFrameNumberRequestPairs.iterator();
1070         while (iter.hasNext()) {
1071             final SimpleEntry<Long, Integer> frameNumberRequestPair = iter.next();
1072             if (frameNumberRequestPair.getKey() <= completedFrameNumber) {
1073
1074                 // remove request from mCaptureListenerMap
1075                 final int requestId = frameNumberRequestPair.getValue();
1076                 final CaptureListenerHolder holder;
1077                 synchronized(mInterfaceLock) {
1078                     if (mRemoteDevice == null) {
1079                         Log.w(TAG, "Camera closed while checking sequences");
1080                         return;
1081                     }
1082
1083                     int index = mCaptureListenerMap.indexOfKey(requestId);
1084                     holder = (index >= 0) ? mCaptureListenerMap.valueAt(index)
1085                             : null;
1086                     if (holder != null) {
1087                         mCaptureListenerMap.removeAt(index);
1088                         if (DEBUG) {
1089                             Log.v(TAG, String.format(
1090                                     "remove holder for requestId %d, "
1091                                     + "because lastFrame %d is <= %d",
1092                                     requestId, frameNumberRequestPair.getKey(),
1093                                     completedFrameNumber));
1094                         }
1095                     }
1096                 }
1097                 iter.remove();
1098
1099                 // Call onCaptureSequenceCompleted
1100                 if (holder != null) {
1101                     Runnable resultDispatch = new Runnable() {
1102                         @Override
1103                         public void run() {
1104                             if (!CameraDeviceImpl.this.isClosed()){
1105                                 if (DEBUG) {
1106                                     Log.d(TAG, String.format(
1107                                             "fire sequence complete for request %d",
1108                                             requestId));
1109                                 }
1110
1111                                 long lastFrameNumber = frameNumberRequestPair.getKey();
1112                                 if (lastFrameNumber < Integer.MIN_VALUE
1113                                         || lastFrameNumber > Integer.MAX_VALUE) {
1114                                     throw new AssertionError(lastFrameNumber
1115                                             + " cannot be cast to int");
1116                                 }
1117                                 holder.getListener().onCaptureSequenceCompleted(
1118                                     CameraDeviceImpl.this,
1119                                     requestId,
1120                                     lastFrameNumber);
1121                             }
1122                         }
1123                     };
1124                     holder.getHandler().post(resultDispatch);
1125                 }
1126
1127             }
1128         }
1129     }
1130
1131     public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
1132
1133         //
1134         // Constants below need to be kept up-to-date with
1135         // frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h
1136         //
1137
1138         //
1139         // Error codes for onCameraError
1140         //
1141
1142         /**
1143          * Camera has been disconnected
1144          */
1145         static final int ERROR_CAMERA_DISCONNECTED = 0;
1146
1147         /**
1148          * Camera has encountered a device-level error
1149          * Matches CameraDevice.StateListener#ERROR_CAMERA_DEVICE
1150          */
1151         static final int ERROR_CAMERA_DEVICE = 1;
1152
1153         /**
1154          * Camera has encountered a service-level error
1155          * Matches CameraDevice.StateListener#ERROR_CAMERA_SERVICE
1156          */
1157         static final int ERROR_CAMERA_SERVICE = 2;
1158
1159         /**
1160          * Camera has encountered an error processing a single request.
1161          */
1162         static final int ERROR_CAMERA_REQUEST = 3;
1163
1164         /**
1165          * Camera has encountered an error producing metadata for a single capture
1166          */
1167         static final int ERROR_CAMERA_RESULT = 4;
1168
1169         /**
1170          * Camera has encountered an error producing an image buffer for a single capture
1171          */
1172         static final int ERROR_CAMERA_BUFFER = 5;
1173
1174         @Override
1175         public IBinder asBinder() {
1176             return this;
1177         }
1178
1179         @Override
1180         public void onDeviceError(final int errorCode, CaptureResultExtras resultExtras) {
1181             if (DEBUG) {
1182                 Log.d(TAG, String.format(
1183                     "Device error received, code %d, frame number %d, request ID %d, subseq ID %d",
1184                     errorCode, resultExtras.getFrameNumber(), resultExtras.getRequestId(),
1185                     resultExtras.getSubsequenceId()));
1186             }
1187
1188             synchronized(mInterfaceLock) {
1189                 if (mRemoteDevice == null) {
1190                     return; // Camera already closed
1191                 }
1192
1193                 switch (errorCode) {
1194                     case ERROR_CAMERA_DISCONNECTED:
1195                         CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected);
1196                         break;
1197                     default:
1198                         Log.e(TAG, "Unknown error from camera device: " + errorCode);
1199                         // no break
1200                     case ERROR_CAMERA_DEVICE:
1201                     case ERROR_CAMERA_SERVICE:
1202                         mInError = true;
1203                         Runnable r = new Runnable() {
1204                             @Override
1205                             public void run() {
1206                                 if (!CameraDeviceImpl.this.isClosed()) {
1207                                     mDeviceListener.onError(CameraDeviceImpl.this, errorCode);
1208                                 }
1209                             }
1210                         };
1211                         CameraDeviceImpl.this.mDeviceHandler.post(r);
1212                         break;
1213                     case ERROR_CAMERA_REQUEST:
1214                     case ERROR_CAMERA_RESULT:
1215                     case ERROR_CAMERA_BUFFER:
1216                         onCaptureErrorLocked(errorCode, resultExtras);
1217                         break;
1218                 }
1219             }
1220         }
1221
1222         @Override
1223         public void onDeviceIdle() {
1224             if (DEBUG) {
1225                 Log.d(TAG, "Camera now idle");
1226             }
1227             synchronized(mInterfaceLock) {
1228                 if (mRemoteDevice == null) return; // Camera already closed
1229
1230                 if (!CameraDeviceImpl.this.mIdle) {
1231                     CameraDeviceImpl.this.mDeviceHandler.post(mCallOnIdle);
1232                 }
1233                 CameraDeviceImpl.this.mIdle = true;
1234             }
1235         }
1236
1237         @Override
1238         public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) {
1239             int requestId = resultExtras.getRequestId();
1240             if (DEBUG) {
1241                 Log.d(TAG, "Capture started for id " + requestId);
1242             }
1243             final CaptureListenerHolder holder;
1244
1245             synchronized(mInterfaceLock) {
1246                 if (mRemoteDevice == null) return; // Camera already closed
1247
1248                 // Get the listener for this frame ID, if there is one
1249                 holder = CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
1250
1251                 if (holder == null) {
1252                     return;
1253                 }
1254
1255                 if (isClosed()) return;
1256
1257                 // Dispatch capture start notice
1258                 holder.getHandler().post(
1259                     new Runnable() {
1260                         @Override
1261                         public void run() {
1262                             if (!CameraDeviceImpl.this.isClosed()) {
1263                                 holder.getListener().onCaptureStarted(
1264                                     CameraDeviceImpl.this,
1265                                     holder.getRequest(resultExtras.getSubsequenceId()),
1266                                     timestamp);
1267                             }
1268                         }
1269                     });
1270
1271             }
1272         }
1273
1274         @Override
1275         public void onResultReceived(CameraMetadataNative result,
1276                 CaptureResultExtras resultExtras) throws RemoteException {
1277
1278             int requestId = resultExtras.getRequestId();
1279             long frameNumber = resultExtras.getFrameNumber();
1280
1281             if (DEBUG) {
1282                 Log.v(TAG, "Received result frame " + frameNumber + " for id "
1283                         + requestId);
1284             }
1285
1286             synchronized(mInterfaceLock) {
1287                 if (mRemoteDevice == null) return; // Camera already closed
1288
1289                 // TODO: Handle CameraCharacteristics access from CaptureResult correctly.
1290                 result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE,
1291                         getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE));
1292
1293                 final CaptureListenerHolder holder =
1294                         CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
1295
1296                 boolean isPartialResult =
1297                         (resultExtras.getPartialResultCount() < mTotalPartialCount);
1298
1299                 // Check if we have a listener for this
1300                 if (holder == null) {
1301                     if (DEBUG) {
1302                         Log.d(TAG,
1303                                 "holder is null, early return at frame "
1304                                         + frameNumber);
1305                     }
1306
1307                     mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult);
1308
1309                     return;
1310                 }
1311
1312                 if (isClosed()) {
1313                     if (DEBUG) {
1314                         Log.d(TAG,
1315                                 "camera is closed, early return at frame "
1316                                         + frameNumber);
1317                     }
1318
1319                     mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult);
1320                     return;
1321                 }
1322
1323                 final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
1324
1325                 Runnable resultDispatch = null;
1326
1327                 CaptureResult finalResult;
1328
1329                 // Either send a partial result or the final capture completed result
1330                 if (isPartialResult) {
1331                     final CaptureResult resultAsCapture =
1332                             new CaptureResult(result, request, resultExtras);
1333
1334                     // Partial result
1335                     resultDispatch = new Runnable() {
1336                         @Override
1337                         public void run() {
1338                             if (!CameraDeviceImpl.this.isClosed()){
1339                                 holder.getListener().onCaptureProgressed(
1340                                     CameraDeviceImpl.this,
1341                                     request,
1342                                     resultAsCapture);
1343                             }
1344                         }
1345                     };
1346
1347                     finalResult = resultAsCapture;
1348                 } else {
1349                     List<CaptureResult> partialResults =
1350                             mFrameNumberTracker.popPartialResults(frameNumber);
1351
1352                     final TotalCaptureResult resultAsCapture =
1353                             new TotalCaptureResult(result, request, resultExtras, partialResults);
1354
1355                     // Final capture result
1356                     resultDispatch = new Runnable() {
1357                         @Override
1358                         public void run() {
1359                             if (!CameraDeviceImpl.this.isClosed()){
1360                                 holder.getListener().onCaptureCompleted(
1361                                     CameraDeviceImpl.this,
1362                                     request,
1363                                     resultAsCapture);
1364                             }
1365                         }
1366                     };
1367
1368                     finalResult = resultAsCapture;
1369                 }
1370
1371                 holder.getHandler().post(resultDispatch);
1372
1373                 // Collect the partials for a total result; or mark the frame as totally completed
1374                 mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult);
1375
1376                 // Fire onCaptureSequenceCompleted
1377                 if (!isPartialResult) {
1378                     checkAndFireSequenceComplete();
1379                 }
1380             }
1381         }
1382
1383         /**
1384          * Called by onDeviceError for handling single-capture failures.
1385          */
1386         private void onCaptureErrorLocked(int errorCode, CaptureResultExtras resultExtras) {
1387
1388             final int requestId = resultExtras.getRequestId();
1389             final int subsequenceId = resultExtras.getSubsequenceId();
1390             final long frameNumber = resultExtras.getFrameNumber();
1391             final CaptureListenerHolder holder =
1392                     CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
1393
1394             final CaptureRequest request = holder.getRequest(subsequenceId);
1395
1396             // No way to report buffer errors right now
1397             if (errorCode == ERROR_CAMERA_BUFFER) {
1398                 Log.e(TAG, String.format("Lost output buffer reported for frame %d", frameNumber));
1399                 return;
1400             }
1401
1402             boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT);
1403
1404             // This is only approximate - exact handling needs the camera service and HAL to
1405             // disambiguate between request failures to due abort and due to real errors.
1406             // For now, assume that if the session believes we're mid-abort, then the error
1407             // is due to abort.
1408             int reason = (mCurrentSession != null && mCurrentSession.isAborting()) ?
1409                     CaptureFailure.REASON_FLUSHED :
1410                     CaptureFailure.REASON_ERROR;
1411
1412             final CaptureFailure failure = new CaptureFailure(
1413                 request,
1414                 reason,
1415                 /*dropped*/ mayHaveBuffers,
1416                 requestId,
1417                 frameNumber);
1418
1419             Runnable failureDispatch = new Runnable() {
1420                 @Override
1421                 public void run() {
1422                     if (!CameraDeviceImpl.this.isClosed()){
1423                         holder.getListener().onCaptureFailed(
1424                             CameraDeviceImpl.this,
1425                             request,
1426                             failure);
1427                     }
1428                 }
1429             };
1430             holder.getHandler().post(failureDispatch);
1431
1432             // Fire onCaptureSequenceCompleted if appropriate
1433             if (DEBUG) {
1434                 Log.v(TAG, String.format("got error frame %d", frameNumber));
1435             }
1436             mFrameNumberTracker.updateTracker(frameNumber, /*error*/true);
1437             checkAndFireSequenceComplete();
1438         }
1439
1440     } // public class CameraDeviceCallbacks
1441
1442     /**
1443      * Default handler management.
1444      *
1445      * <p>
1446      * If handler is null, get the current thread's
1447      * Looper to create a Handler with. If no looper exists, throw {@code IllegalArgumentException}.
1448      * </p>
1449      */
1450     static Handler checkHandler(Handler handler) {
1451         if (handler == null) {
1452             Looper looper = Looper.myLooper();
1453             if (looper == null) {
1454                 throw new IllegalArgumentException(
1455                     "No handler given, and current thread has no looper!");
1456             }
1457             handler = new Handler(looper);
1458         }
1459         return handler;
1460     }
1461
1462     /**
1463      * Default handler management, conditional on there being a listener.
1464      *
1465      * <p>If the listener isn't null, check the handler, otherwise pass it through.</p>
1466      */
1467     static <T> Handler checkHandler(Handler handler, T listener) {
1468         if (listener != null) {
1469             return checkHandler(handler);
1470         }
1471         return handler;
1472     }
1473
1474     private void checkIfCameraClosedOrInError() throws CameraAccessException {
1475         if (mInError) {
1476             throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
1477                     "The camera device has encountered a serious error");
1478         }
1479         if (mRemoteDevice == null) {
1480             throw new IllegalStateException("CameraDevice was already closed");
1481         }
1482     }
1483
1484     /** Whether the camera device has started to close (may not yet have finished) */
1485     private boolean isClosed() {
1486         return mClosing;
1487     }
1488
1489     private CameraCharacteristics getCharacteristics() {
1490         return mCharacteristics;
1491     }
1492 }