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