CameraDeviceImpl.java revision 51dcfd65a6742884e07182dd7d13b916fd4e0305
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.utils.CameraBinderDecorator;
32import android.hardware.camera2.utils.CameraRuntimeException;
33import android.hardware.camera2.utils.LongParcelable;
34import android.os.Handler;
35import android.os.IBinder;
36import android.os.Looper;
37import android.os.RemoteException;
38import android.util.Log;
39import android.util.SparseArray;
40import android.view.Surface;
41
42import java.util.AbstractMap.SimpleEntry;
43import java.util.ArrayList;
44import java.util.HashMap;
45import java.util.HashSet;
46import java.util.Iterator;
47import java.util.List;
48import java.util.TreeSet;
49
50/**
51 * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
52 */
53public class CameraDeviceImpl extends CameraDevice {
54    private final String TAG;
55    private final boolean DEBUG;
56
57    private static final int REQUEST_ID_NONE = -1;
58
59    // TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
60    private ICameraDeviceUser mRemoteDevice;
61
62    // Lock to synchronize cross-thread access to device public interface
63    final Object mInterfaceLock = new Object(); // access from this class and Session only!
64    private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
65
66    private final StateCallback mDeviceCallback;
67    private volatile StateCallbackKK mSessionStateCallback;
68    private final Handler mDeviceHandler;
69
70    private volatile boolean mClosing = false;
71    private boolean mInError = false;
72    private boolean mIdle = true;
73
74    /** map request IDs to callback/request data */
75    private final SparseArray<CaptureCallbackHolder> mCaptureCallbackMap =
76            new SparseArray<CaptureCallbackHolder>();
77
78    private int mRepeatingRequestId = REQUEST_ID_NONE;
79    private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>();
80    // Map stream IDs to Surfaces
81    private final SparseArray<Surface> mConfiguredOutputs = new SparseArray<Surface>();
82
83    private final String mCameraId;
84    private final CameraCharacteristics mCharacteristics;
85    private final int mTotalPartialCount;
86
87    /**
88     * A list tracking request and its expected last frame.
89     * Updated when calling ICameraDeviceUser methods.
90     */
91    private final List<SimpleEntry</*frameNumber*/Long, /*requestId*/Integer>>
92            mFrameNumberRequestPairs = new ArrayList<SimpleEntry<Long, Integer>>();
93
94    /**
95     * An object tracking received frame numbers.
96     * Updated when receiving callbacks from ICameraDeviceCallbacks.
97     */
98    private final FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker();
99
100    private CameraCaptureSessionImpl mCurrentSession;
101    private int mNextSessionId = 0;
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            StateCallbackKK sessionCallback = null;
110            synchronized(mInterfaceLock) {
111                if (mRemoteDevice == null) return; // Camera already closed
112
113                sessionCallback = mSessionStateCallback;
114            }
115            if (sessionCallback != null) {
116                sessionCallback.onOpened(CameraDeviceImpl.this);
117            }
118            mDeviceCallback.onOpened(CameraDeviceImpl.this);
119        }
120    };
121
122    private final Runnable mCallOnUnconfigured = new Runnable() {
123        @Override
124        public void run() {
125            StateCallbackKK sessionCallback = null;
126            synchronized(mInterfaceLock) {
127                if (mRemoteDevice == null) return; // Camera already closed
128
129                sessionCallback = mSessionStateCallback;
130            }
131            if (sessionCallback != null) {
132                sessionCallback.onUnconfigured(CameraDeviceImpl.this);
133            }
134        }
135    };
136
137    private final Runnable mCallOnActive = new Runnable() {
138        @Override
139        public void run() {
140            StateCallbackKK sessionCallback = null;
141            synchronized(mInterfaceLock) {
142                if (mRemoteDevice == null) return; // Camera already closed
143
144                sessionCallback = mSessionStateCallback;
145            }
146            if (sessionCallback != null) {
147                sessionCallback.onActive(CameraDeviceImpl.this);
148            }
149        }
150    };
151
152    private final Runnable mCallOnBusy = new Runnable() {
153        @Override
154        public void run() {
155            StateCallbackKK sessionCallback = null;
156            synchronized(mInterfaceLock) {
157                if (mRemoteDevice == null) return; // Camera already closed
158
159                sessionCallback = mSessionStateCallback;
160            }
161            if (sessionCallback != null) {
162                sessionCallback.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            StateCallbackKK sessionCallback = null;
176            synchronized(mInterfaceLock) {
177                sessionCallback = mSessionStateCallback;
178            }
179            if (sessionCallback != null) {
180                sessionCallback.onClosed(CameraDeviceImpl.this);
181            }
182            mDeviceCallback.onClosed(CameraDeviceImpl.this);
183            mClosedOnce = true;
184        }
185    };
186
187    private final Runnable mCallOnIdle = new Runnable() {
188        @Override
189        public void run() {
190            StateCallbackKK sessionCallback = null;
191            synchronized(mInterfaceLock) {
192                if (mRemoteDevice == null) return; // Camera already closed
193
194                sessionCallback = mSessionStateCallback;
195            }
196            if (sessionCallback != null) {
197                sessionCallback.onIdle(CameraDeviceImpl.this);
198            }
199        }
200    };
201
202    private final Runnable mCallOnDisconnected = new Runnable() {
203        @Override
204        public void run() {
205            StateCallbackKK sessionCallback = null;
206            synchronized(mInterfaceLock) {
207                if (mRemoteDevice == null) return; // Camera already closed
208
209                sessionCallback = mSessionStateCallback;
210            }
211            if (sessionCallback != null) {
212                sessionCallback.onDisconnected(CameraDeviceImpl.this);
213            }
214            mDeviceCallback.onDisconnected(CameraDeviceImpl.this);
215        }
216    };
217
218    public CameraDeviceImpl(String cameraId, StateCallback callback, Handler handler,
219                        CameraCharacteristics characteristics) {
220        if (cameraId == null || callback == null || handler == null || characteristics == null) {
221            throw new IllegalArgumentException("Null argument given");
222        }
223        mCameraId = cameraId;
224        mDeviceCallback = callback;
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 callback.
267     * Use in place of setRemoteDevice() when startup fails.</p>
268     */
269    public void setRemoteFailure(final CameraRuntimeException failure) {
270        int failureCode = StateCallback.ERROR_CAMERA_DEVICE;
271        boolean failureIsError = true;
272
273        switch (failure.getReason()) {
274            case CameraAccessException.CAMERA_IN_USE:
275                failureCode = StateCallback.ERROR_CAMERA_IN_USE;
276                break;
277            case CameraAccessException.MAX_CAMERAS_IN_USE:
278                failureCode = StateCallback.ERROR_MAX_CAMERAS_IN_USE;
279                break;
280            case CameraAccessException.CAMERA_DISABLED:
281                failureCode = StateCallback.ERROR_CAMERA_DISABLED;
282                break;
283            case CameraAccessException.CAMERA_DISCONNECTED:
284                failureIsError = false;
285                break;
286            case CameraAccessException.CAMERA_ERROR:
287                failureCode = StateCallback.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                        mDeviceCallback.onError(CameraDeviceImpl.this, code);
302                    } else {
303                        mDeviceCallback.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.StateCallback callback, 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(mNextSessionId++,
449                            outputs, callback, handler, this, mDeviceHandler,
450                            configureSuccess);
451
452            // TODO: wait until current session closes, then create the new session
453            mCurrentSession = newSession;
454
455            if (pendingException != null) {
456                throw pendingException;
457            }
458
459            mSessionStateCallback = mCurrentSession.getDeviceStateCallback();
460        }
461    }
462
463    /**
464     * For use by backwards-compatibility code only.
465     */
466    public void setSessionListener(StateCallbackKK sessionCallback) {
467        synchronized(mInterfaceLock) {
468            mSessionStateCallback = sessionCallback;
469        }
470    }
471
472    @Override
473    public CaptureRequest.Builder createCaptureRequest(int templateType)
474            throws CameraAccessException {
475        synchronized(mInterfaceLock) {
476            checkIfCameraClosedOrInError();
477
478            CameraMetadataNative templatedRequest = new CameraMetadataNative();
479
480            try {
481                mRemoteDevice.createDefaultRequest(templateType, /*out*/templatedRequest);
482            } catch (CameraRuntimeException e) {
483                throw e.asChecked();
484            } catch (RemoteException e) {
485                // impossible
486                return null;
487            }
488
489            CaptureRequest.Builder builder =
490                    new CaptureRequest.Builder(templatedRequest);
491
492            return builder;
493        }
494    }
495
496    public int capture(CaptureRequest request, CaptureCallback callback, Handler handler)
497            throws CameraAccessException {
498        if (DEBUG) {
499            Log.d(TAG, "calling capture");
500        }
501        List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
502        requestList.add(request);
503        return submitCaptureRequest(requestList, callback, handler, /*streaming*/false);
504    }
505
506    public int captureBurst(List<CaptureRequest> requests, CaptureCallback callback,
507            Handler handler) throws CameraAccessException {
508        if (requests == null || requests.isEmpty()) {
509            throw new IllegalArgumentException("At least one request must be given");
510        }
511        return submitCaptureRequest(requests, callback, handler, /*streaming*/false);
512    }
513
514    /**
515     * This method checks lastFrameNumber returned from ICameraDeviceUser methods for
516     * starting and stopping repeating request and flushing.
517     *
518     * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never
519     * sent to HAL. Then onCaptureSequenceAborted is immediately triggered.
520     * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber pair
521     * is added to the list mFrameNumberRequestPairs.</p>
522     *
523     * @param requestId the request ID of the current repeating request.
524     *
525     * @param lastFrameNumber last frame number returned from binder.
526     */
527    private void checkEarlyTriggerSequenceComplete(
528            final int requestId, final long lastFrameNumber) {
529        // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request
530        // was never sent to HAL. Should trigger onCaptureSequenceAborted immediately.
531        if (lastFrameNumber == CaptureCallback.NO_FRAMES_CAPTURED) {
532            final CaptureCallbackHolder holder;
533            int index = mCaptureCallbackMap.indexOfKey(requestId);
534            holder = (index >= 0) ? mCaptureCallbackMap.valueAt(index) : null;
535            if (holder != null) {
536                mCaptureCallbackMap.removeAt(index);
537                if (DEBUG) {
538                    Log.v(TAG, String.format(
539                            "remove holder for requestId %d, "
540                            + "because lastFrame is %d.",
541                            requestId, lastFrameNumber));
542                }
543            }
544
545            if (holder != null) {
546                if (DEBUG) {
547                    Log.v(TAG, "immediately trigger onCaptureSequenceAborted because"
548                            + " request did not reach HAL");
549                }
550
551                Runnable resultDispatch = new Runnable() {
552                    @Override
553                    public void run() {
554                        if (!CameraDeviceImpl.this.isClosed()) {
555                            if (DEBUG) {
556                                Log.d(TAG, String.format(
557                                        "early trigger sequence complete for request %d",
558                                        requestId));
559                            }
560                            if (lastFrameNumber < Integer.MIN_VALUE
561                                    || lastFrameNumber > Integer.MAX_VALUE) {
562                                throw new AssertionError(lastFrameNumber + " cannot be cast to int");
563                            }
564                            holder.getCallback().onCaptureSequenceAborted(
565                                    CameraDeviceImpl.this,
566                                    requestId);
567                        }
568                    }
569                };
570                holder.getHandler().post(resultDispatch);
571            } else {
572                Log.w(TAG, String.format(
573                        "did not register callback to request %d",
574                        requestId));
575            }
576        } else {
577            mFrameNumberRequestPairs.add(
578                    new SimpleEntry<Long, Integer>(lastFrameNumber,
579                            requestId));
580            // It is possible that the last frame has already arrived, so we need to check
581            // for sequence completion right away
582            checkAndFireSequenceComplete();
583        }
584    }
585
586    private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback,
587            Handler handler, boolean repeating) throws CameraAccessException {
588
589        // Need a valid handler, or current thread needs to have a looper, if
590        // callback is valid
591        handler = checkHandler(handler, callback);
592
593        // Make sure that there all requests have at least 1 surface; all surfaces are non-null
594        for (CaptureRequest request : requestList) {
595            if (request.getTargets().isEmpty()) {
596                throw new IllegalArgumentException(
597                        "Each request must have at least one Surface target");
598            }
599
600            for (Surface surface : request.getTargets()) {
601                if (surface == null) {
602                    throw new IllegalArgumentException("Null Surface targets are not allowed");
603                }
604            }
605        }
606
607        synchronized(mInterfaceLock) {
608            checkIfCameraClosedOrInError();
609            int requestId;
610
611            if (repeating) {
612                stopRepeating();
613            }
614
615            LongParcelable lastFrameNumberRef = new LongParcelable();
616            try {
617                requestId = mRemoteDevice.submitRequestList(requestList, repeating,
618                        /*out*/lastFrameNumberRef);
619                if (DEBUG) {
620                    Log.v(TAG, "last frame number " + lastFrameNumberRef.getNumber());
621                }
622            } catch (CameraRuntimeException e) {
623                throw e.asChecked();
624            } catch (RemoteException e) {
625                // impossible
626                return -1;
627            }
628
629            if (callback != null) {
630                mCaptureCallbackMap.put(requestId, new CaptureCallbackHolder(callback,
631                        requestList, handler, repeating));
632            } else {
633                if (DEBUG) {
634                    Log.d(TAG, "Listen for request " + requestId + " is null");
635                }
636            }
637
638            long lastFrameNumber = lastFrameNumberRef.getNumber();
639
640            if (repeating) {
641                if (mRepeatingRequestId != REQUEST_ID_NONE) {
642                    checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
643                }
644                mRepeatingRequestId = requestId;
645            } else {
646                mFrameNumberRequestPairs.add(
647                        new SimpleEntry<Long, Integer>(lastFrameNumber, requestId));
648            }
649
650            if (mIdle) {
651                mDeviceHandler.post(mCallOnActive);
652            }
653            mIdle = false;
654
655            return requestId;
656        }
657    }
658
659    public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback,
660            Handler handler) throws CameraAccessException {
661        List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
662        requestList.add(request);
663        return submitCaptureRequest(requestList, callback, handler, /*streaming*/true);
664    }
665
666    public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback callback,
667            Handler handler) throws CameraAccessException {
668        if (requests == null || requests.isEmpty()) {
669            throw new IllegalArgumentException("At least one request must be given");
670        }
671        return submitCaptureRequest(requests, callback, handler, /*streaming*/true);
672    }
673
674    public void stopRepeating() throws CameraAccessException {
675
676        synchronized(mInterfaceLock) {
677            checkIfCameraClosedOrInError();
678            if (mRepeatingRequestId != REQUEST_ID_NONE) {
679
680                int requestId = mRepeatingRequestId;
681                mRepeatingRequestId = REQUEST_ID_NONE;
682
683                // Queue for deletion after in-flight requests finish
684                if (mCaptureCallbackMap.get(requestId) != null) {
685                    mRepeatingRequestIdDeletedList.add(requestId);
686                }
687
688                try {
689                    LongParcelable lastFrameNumberRef = new LongParcelable();
690                    mRemoteDevice.cancelRequest(requestId, /*out*/lastFrameNumberRef);
691                    long lastFrameNumber = lastFrameNumberRef.getNumber();
692
693                    checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber);
694
695                } catch (CameraRuntimeException e) {
696                    throw e.asChecked();
697                } catch (RemoteException e) {
698                    // impossible
699                    return;
700                }
701            }
702        }
703    }
704
705    private void waitUntilIdle() throws CameraAccessException {
706
707        synchronized(mInterfaceLock) {
708            checkIfCameraClosedOrInError();
709
710            if (mRepeatingRequestId != REQUEST_ID_NONE) {
711                throw new IllegalStateException("Active repeating request ongoing");
712            }
713            try {
714                mRemoteDevice.waitUntilIdle();
715            } catch (CameraRuntimeException e) {
716                throw e.asChecked();
717            } catch (RemoteException e) {
718                // impossible
719                return;
720            }
721        }
722    }
723
724    public void flush() throws CameraAccessException {
725        synchronized(mInterfaceLock) {
726            checkIfCameraClosedOrInError();
727
728            mDeviceHandler.post(mCallOnBusy);
729
730            // If already idle, just do a busy->idle transition immediately, don't actually
731            // flush.
732            if (mIdle) {
733                mDeviceHandler.post(mCallOnIdle);
734                return;
735            }
736            try {
737                LongParcelable lastFrameNumberRef = new LongParcelable();
738                mRemoteDevice.flush(/*out*/lastFrameNumberRef);
739                if (mRepeatingRequestId != REQUEST_ID_NONE) {
740                    long lastFrameNumber = lastFrameNumberRef.getNumber();
741                    checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
742                    mRepeatingRequestId = REQUEST_ID_NONE;
743                }
744            } catch (CameraRuntimeException e) {
745                throw e.asChecked();
746            } catch (RemoteException e) {
747                // impossible
748                return;
749            }
750        }
751    }
752
753    @Override
754    public void close() {
755        synchronized (mInterfaceLock) {
756            try {
757                if (mRemoteDevice != null) {
758                    mRemoteDevice.disconnect();
759                }
760            } catch (CameraRuntimeException e) {
761                Log.e(TAG, "Exception while closing: ", e.asChecked());
762            } catch (RemoteException e) {
763                // impossible
764            }
765
766            // Only want to fire the onClosed callback once;
767            // either a normal close where the remote device is valid
768            // or a close after a startup error (no remote device but in error state)
769            if (mRemoteDevice != null || mInError) {
770                mDeviceHandler.post(mCallOnClosed);
771            }
772
773            mRemoteDevice = null;
774            mInError = false;
775        }
776    }
777
778    @Override
779    protected void finalize() throws Throwable {
780        try {
781            close();
782        }
783        finally {
784            super.finalize();
785        }
786    }
787
788    /**
789     * <p>A callback for tracking the progress of a {@link CaptureRequest}
790     * submitted to the camera device.</p>
791     *
792     */
793    public static abstract class CaptureCallback {
794
795        /**
796         * This constant is used to indicate that no images were captured for
797         * the request.
798         *
799         * @hide
800         */
801        public static final int NO_FRAMES_CAPTURED = -1;
802
803        /**
804         * This method is called when the camera device has started capturing
805         * the output image for the request, at the beginning of image exposure.
806         *
807         * @see android.media.MediaActionSound
808         */
809        public void onCaptureStarted(CameraDevice camera,
810                CaptureRequest request, long timestamp, long frameNumber) {
811            // default empty implementation
812        }
813
814        /**
815         * This method is called when some results from an image capture are
816         * available.
817         *
818         * @hide
819         */
820        public void onCapturePartial(CameraDevice camera,
821                CaptureRequest request, CaptureResult result) {
822            // default empty implementation
823        }
824
825        /**
826         * This method is called when an image capture makes partial forward progress; some
827         * (but not all) results from an image capture are available.
828         *
829         */
830        public void onCaptureProgressed(CameraDevice camera,
831                CaptureRequest request, CaptureResult partialResult) {
832            // default empty implementation
833        }
834
835        /**
836         * This method is called when an image capture has fully completed and all the
837         * result metadata is available.
838         */
839        public void onCaptureCompleted(CameraDevice camera,
840                CaptureRequest request, TotalCaptureResult result) {
841            // default empty implementation
842        }
843
844        /**
845         * This method is called instead of {@link #onCaptureCompleted} when the
846         * camera device failed to produce a {@link CaptureResult} for the
847         * request.
848         */
849        public void onCaptureFailed(CameraDevice camera,
850                CaptureRequest request, CaptureFailure failure) {
851            // default empty implementation
852        }
853
854        /**
855         * This method is called independently of the others in CaptureCallback,
856         * when a capture sequence finishes and all {@link CaptureResult}
857         * or {@link CaptureFailure} for it have been returned via this callback.
858         */
859        public void onCaptureSequenceCompleted(CameraDevice camera,
860                int sequenceId, long frameNumber) {
861            // default empty implementation
862        }
863
864        /**
865         * This method is called independently of the others in CaptureCallback,
866         * when a capture sequence aborts before any {@link CaptureResult}
867         * or {@link CaptureFailure} for it have been returned via this callback.
868         */
869        public void onCaptureSequenceAborted(CameraDevice camera,
870                int sequenceId) {
871            // default empty implementation
872        }
873    }
874
875    /**
876     * A callback for notifications about the state of a camera device, adding in the callbacks that
877     * were part of the earlier KK API design, but now only used internally.
878     */
879    public static abstract class StateCallbackKK extends StateCallback {
880        /**
881         * The method called when a camera device has no outputs configured.
882         *
883         */
884        public void onUnconfigured(CameraDevice camera) {
885            // Default empty implementation
886        }
887
888        /**
889         * The method called when a camera device begins processing
890         * {@link CaptureRequest capture requests}.
891         *
892         */
893        public void onActive(CameraDevice camera) {
894            // Default empty implementation
895        }
896
897        /**
898         * The method called when a camera device is busy.
899         *
900         */
901        public void onBusy(CameraDevice camera) {
902            // Default empty implementation
903        }
904
905        /**
906         * The method called when a camera device has finished processing all
907         * submitted capture requests and has reached an idle state.
908         *
909         */
910        public void onIdle(CameraDevice camera) {
911            // Default empty implementation
912        }
913    }
914
915    static class CaptureCallbackHolder {
916
917        private final boolean mRepeating;
918        private final CaptureCallback mCallback;
919        private final List<CaptureRequest> mRequestList;
920        private final Handler mHandler;
921
922        CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList,
923                Handler handler, boolean repeating) {
924            if (callback == null || handler == null) {
925                throw new UnsupportedOperationException(
926                    "Must have a valid handler and a valid callback");
927            }
928            mRepeating = repeating;
929            mHandler = handler;
930            mRequestList = new ArrayList<CaptureRequest>(requestList);
931            mCallback = callback;
932        }
933
934        public boolean isRepeating() {
935            return mRepeating;
936        }
937
938        public CaptureCallback getCallback() {
939            return mCallback;
940        }
941
942        public CaptureRequest getRequest(int subsequenceId) {
943            if (subsequenceId >= mRequestList.size()) {
944                throw new IllegalArgumentException(
945                        String.format(
946                                "Requested subsequenceId %d is larger than request list size %d.",
947                                subsequenceId, mRequestList.size()));
948            } else {
949                if (subsequenceId < 0) {
950                    throw new IllegalArgumentException(String.format(
951                            "Requested subsequenceId %d is negative", subsequenceId));
952                } else {
953                    return mRequestList.get(subsequenceId);
954                }
955            }
956        }
957
958        public CaptureRequest getRequest() {
959            return getRequest(0);
960        }
961
962        public Handler getHandler() {
963            return mHandler;
964        }
965
966    }
967
968    /**
969     * This class tracks the last frame number for submitted requests.
970     */
971    public class FrameNumberTracker {
972
973        private long mCompletedFrameNumber = -1;
974        private final TreeSet<Long> mFutureErrorSet = new TreeSet<Long>();
975        /** Map frame numbers to list of partial results */
976        private final HashMap<Long, List<CaptureResult>> mPartialResults = new HashMap<>();
977
978        private void update() {
979            Iterator<Long> iter = mFutureErrorSet.iterator();
980            while (iter.hasNext()) {
981                long errorFrameNumber = iter.next();
982                if (errorFrameNumber == mCompletedFrameNumber + 1) {
983                    mCompletedFrameNumber++;
984                    iter.remove();
985                } else {
986                    break;
987                }
988            }
989        }
990
991        /**
992         * This function is called every time when a result or an error is received.
993         * @param frameNumber the frame number corresponding to the result or error
994         * @param isError true if it is an error, false if it is not an error
995         */
996        public void updateTracker(long frameNumber, boolean isError) {
997            if (isError) {
998                mFutureErrorSet.add(frameNumber);
999            } else {
1000                /**
1001                 * HAL cannot send an OnResultReceived for frame N unless it knows for
1002                 * sure that all frames prior to N have either errored out or completed.
1003                 * So if the current frame is not an error, then all previous frames
1004                 * should have arrived. The following line checks whether this holds.
1005                 */
1006                if (frameNumber != mCompletedFrameNumber + 1) {
1007                    Log.e(TAG, String.format(
1008                            "result frame number %d comes out of order, should be %d + 1",
1009                            frameNumber, mCompletedFrameNumber));
1010                    // Continue on to set the completed frame number to this frame anyway,
1011                    // to be robust to lower-level errors and allow for clean shutdowns.
1012                }
1013                mCompletedFrameNumber = frameNumber;
1014            }
1015            update();
1016        }
1017
1018        /**
1019         * This function is called every time a result has been completed.
1020         *
1021         * <p>It keeps a track of all the partial results already created for a particular
1022         * frame number.</p>
1023         *
1024         * @param frameNumber the frame number corresponding to the result
1025         * @param result the total or partial result
1026         * @param partial {@true} if the result is partial, {@code false} if total
1027         */
1028        public void updateTracker(long frameNumber, CaptureResult result, boolean partial) {
1029
1030            if (!partial) {
1031                // Update the total result's frame status as being successful
1032                updateTracker(frameNumber, /*isError*/false);
1033                // Don't keep a list of total results, we don't need to track them
1034                return;
1035            }
1036
1037            if (result == null) {
1038                // Do not record blank results; this also means there will be no total result
1039                // so it doesn't matter that the partials were not recorded
1040                return;
1041            }
1042
1043            // Partial results must be aggregated in-order for that frame number
1044            List<CaptureResult> partials = mPartialResults.get(frameNumber);
1045            if (partials == null) {
1046                partials = new ArrayList<>();
1047                mPartialResults.put(frameNumber, partials);
1048            }
1049
1050            partials.add(result);
1051        }
1052
1053        /**
1054         * Attempt to pop off all of the partial results seen so far for the {@code frameNumber}.
1055         *
1056         * <p>Once popped-off, the partial results are forgotten (unless {@code updateTracker}
1057         * is called again with new partials for that frame number).</p>
1058         *
1059         * @param frameNumber the frame number corresponding to the result
1060         * @return a list of partial results for that frame with at least 1 element,
1061         *         or {@code null} if there were no partials recorded for that frame
1062         */
1063        public List<CaptureResult> popPartialResults(long frameNumber) {
1064            return mPartialResults.remove(frameNumber);
1065        }
1066
1067        public long getCompletedFrameNumber() {
1068            return mCompletedFrameNumber;
1069        }
1070
1071    }
1072
1073    private void checkAndFireSequenceComplete() {
1074        long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
1075        Iterator<SimpleEntry<Long, Integer> > iter = mFrameNumberRequestPairs.iterator();
1076        while (iter.hasNext()) {
1077            final SimpleEntry<Long, Integer> frameNumberRequestPair = iter.next();
1078            if (frameNumberRequestPair.getKey() <= completedFrameNumber) {
1079
1080                // remove request from mCaptureCallbackMap
1081                final int requestId = frameNumberRequestPair.getValue();
1082                final CaptureCallbackHolder holder;
1083                synchronized(mInterfaceLock) {
1084                    if (mRemoteDevice == null) {
1085                        Log.w(TAG, "Camera closed while checking sequences");
1086                        return;
1087                    }
1088
1089                    int index = mCaptureCallbackMap.indexOfKey(requestId);
1090                    holder = (index >= 0) ? mCaptureCallbackMap.valueAt(index)
1091                            : null;
1092                    if (holder != null) {
1093                        mCaptureCallbackMap.removeAt(index);
1094                        if (DEBUG) {
1095                            Log.v(TAG, String.format(
1096                                    "remove holder for requestId %d, "
1097                                    + "because lastFrame %d is <= %d",
1098                                    requestId, frameNumberRequestPair.getKey(),
1099                                    completedFrameNumber));
1100                        }
1101                    }
1102                }
1103                iter.remove();
1104
1105                // Call onCaptureSequenceCompleted
1106                if (holder != null) {
1107                    Runnable resultDispatch = new Runnable() {
1108                        @Override
1109                        public void run() {
1110                            if (!CameraDeviceImpl.this.isClosed()){
1111                                if (DEBUG) {
1112                                    Log.d(TAG, String.format(
1113                                            "fire sequence complete for request %d",
1114                                            requestId));
1115                                }
1116
1117                                long lastFrameNumber = frameNumberRequestPair.getKey();
1118                                if (lastFrameNumber < Integer.MIN_VALUE
1119                                        || lastFrameNumber > Integer.MAX_VALUE) {
1120                                    throw new AssertionError(lastFrameNumber
1121                                            + " cannot be cast to int");
1122                                }
1123                                holder.getCallback().onCaptureSequenceCompleted(
1124                                    CameraDeviceImpl.this,
1125                                    requestId,
1126                                    lastFrameNumber);
1127                            }
1128                        }
1129                    };
1130                    holder.getHandler().post(resultDispatch);
1131                }
1132
1133            }
1134        }
1135    }
1136
1137    public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
1138        //
1139        // Constants below need to be kept up-to-date with
1140        // frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h
1141        //
1142
1143        //
1144        // Error codes for onCameraError
1145        //
1146
1147        /**
1148         * Camera has been disconnected
1149         */
1150        public static final int ERROR_CAMERA_DISCONNECTED = 0;
1151        /**
1152         * Camera has encountered a device-level error
1153         * Matches CameraDevice.StateCallback#ERROR_CAMERA_DEVICE
1154         */
1155        public static final int ERROR_CAMERA_DEVICE = 1;
1156        /**
1157         * Camera has encountered a service-level error
1158         * Matches CameraDevice.StateCallback#ERROR_CAMERA_SERVICE
1159         */
1160        public static final int ERROR_CAMERA_SERVICE = 2;
1161        /**
1162         * Camera has encountered an error processing a single request.
1163         */
1164        public static final int ERROR_CAMERA_REQUEST = 3;
1165        /**
1166         * Camera has encountered an error producing metadata for a single capture
1167         */
1168        public static final int ERROR_CAMERA_RESULT = 4;
1169        /**
1170         * Camera has encountered an error producing an image buffer for a single capture
1171         */
1172        public 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                                    mDeviceCallback.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            final long frameNumber = resultExtras.getFrameNumber();
1241
1242            if (DEBUG) {
1243                Log.d(TAG, "Capture started for id " + requestId + " frame number " + frameNumber);
1244            }
1245            final CaptureCallbackHolder holder;
1246
1247            synchronized(mInterfaceLock) {
1248                if (mRemoteDevice == null) return; // Camera already closed
1249
1250                // Get the callback for this frame ID, if there is one
1251                holder = CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId);
1252
1253                if (holder == null) {
1254                    return;
1255                }
1256
1257                if (isClosed()) return;
1258
1259                // Dispatch capture start notice
1260                holder.getHandler().post(
1261                    new Runnable() {
1262                        @Override
1263                        public void run() {
1264                            if (!CameraDeviceImpl.this.isClosed()) {
1265                                holder.getCallback().onCaptureStarted(
1266                                    CameraDeviceImpl.this,
1267                                    holder.getRequest(resultExtras.getSubsequenceId()),
1268                                    timestamp, frameNumber);
1269                            }
1270                        }
1271                    });
1272
1273            }
1274        }
1275
1276        @Override
1277        public void onResultReceived(CameraMetadataNative result,
1278                CaptureResultExtras resultExtras) throws RemoteException {
1279
1280            int requestId = resultExtras.getRequestId();
1281            long frameNumber = resultExtras.getFrameNumber();
1282
1283            if (DEBUG) {
1284                Log.v(TAG, "Received result frame " + frameNumber + " for id "
1285                        + requestId);
1286            }
1287
1288            synchronized(mInterfaceLock) {
1289                if (mRemoteDevice == null) return; // Camera already closed
1290
1291                // TODO: Handle CameraCharacteristics access from CaptureResult correctly.
1292                result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE,
1293                        getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE));
1294
1295                final CaptureCallbackHolder holder =
1296                        CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId);
1297
1298                boolean isPartialResult =
1299                        (resultExtras.getPartialResultCount() < mTotalPartialCount);
1300
1301                // Check if we have a callback for this
1302                if (holder == null) {
1303                    if (DEBUG) {
1304                        Log.d(TAG,
1305                                "holder is null, early return at frame "
1306                                        + frameNumber);
1307                    }
1308
1309                    mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult);
1310
1311                    return;
1312                }
1313
1314                if (isClosed()) {
1315                    if (DEBUG) {
1316                        Log.d(TAG,
1317                                "camera is closed, early return at frame "
1318                                        + frameNumber);
1319                    }
1320
1321                    mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult);
1322                    return;
1323                }
1324
1325                final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
1326
1327                Runnable resultDispatch = null;
1328
1329                CaptureResult finalResult;
1330
1331                // Either send a partial result or the final capture completed result
1332                if (isPartialResult) {
1333                    final CaptureResult resultAsCapture =
1334                            new CaptureResult(result, request, resultExtras);
1335
1336                    // Partial result
1337                    resultDispatch = new Runnable() {
1338                        @Override
1339                        public void run() {
1340                            if (!CameraDeviceImpl.this.isClosed()){
1341                                holder.getCallback().onCaptureProgressed(
1342                                    CameraDeviceImpl.this,
1343                                    request,
1344                                    resultAsCapture);
1345                            }
1346                        }
1347                    };
1348
1349                    finalResult = resultAsCapture;
1350                } else {
1351                    List<CaptureResult> partialResults =
1352                            mFrameNumberTracker.popPartialResults(frameNumber);
1353
1354                    final TotalCaptureResult resultAsCapture =
1355                            new TotalCaptureResult(result, request, resultExtras, partialResults);
1356
1357                    // Final capture result
1358                    resultDispatch = new Runnable() {
1359                        @Override
1360                        public void run() {
1361                            if (!CameraDeviceImpl.this.isClosed()){
1362                                holder.getCallback().onCaptureCompleted(
1363                                    CameraDeviceImpl.this,
1364                                    request,
1365                                    resultAsCapture);
1366                            }
1367                        }
1368                    };
1369
1370                    finalResult = resultAsCapture;
1371                }
1372
1373                holder.getHandler().post(resultDispatch);
1374
1375                // Collect the partials for a total result; or mark the frame as totally completed
1376                mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult);
1377
1378                // Fire onCaptureSequenceCompleted
1379                if (!isPartialResult) {
1380                    checkAndFireSequenceComplete();
1381                }
1382            }
1383        }
1384
1385        /**
1386         * Called by onDeviceError for handling single-capture failures.
1387         */
1388        private void onCaptureErrorLocked(int errorCode, CaptureResultExtras resultExtras) {
1389
1390            final int requestId = resultExtras.getRequestId();
1391            final int subsequenceId = resultExtras.getSubsequenceId();
1392            final long frameNumber = resultExtras.getFrameNumber();
1393            final CaptureCallbackHolder holder =
1394                    CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId);
1395
1396            final CaptureRequest request = holder.getRequest(subsequenceId);
1397
1398            // No way to report buffer errors right now
1399            if (errorCode == ERROR_CAMERA_BUFFER) {
1400                Log.e(TAG, String.format("Lost output buffer reported for frame %d", frameNumber));
1401                return;
1402            }
1403
1404            boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT);
1405
1406            // This is only approximate - exact handling needs the camera service and HAL to
1407            // disambiguate between request failures to due abort and due to real errors.
1408            // For now, assume that if the session believes we're mid-abort, then the error
1409            // is due to abort.
1410            int reason = (mCurrentSession != null && mCurrentSession.isAborting()) ?
1411                    CaptureFailure.REASON_FLUSHED :
1412                    CaptureFailure.REASON_ERROR;
1413
1414            final CaptureFailure failure = new CaptureFailure(
1415                request,
1416                reason,
1417                /*dropped*/ mayHaveBuffers,
1418                requestId,
1419                frameNumber);
1420
1421            Runnable failureDispatch = new Runnable() {
1422                @Override
1423                public void run() {
1424                    if (!CameraDeviceImpl.this.isClosed()){
1425                        holder.getCallback().onCaptureFailed(
1426                            CameraDeviceImpl.this,
1427                            request,
1428                            failure);
1429                    }
1430                }
1431            };
1432            holder.getHandler().post(failureDispatch);
1433
1434            // Fire onCaptureSequenceCompleted if appropriate
1435            if (DEBUG) {
1436                Log.v(TAG, String.format("got error frame %d", frameNumber));
1437            }
1438            mFrameNumberTracker.updateTracker(frameNumber, /*error*/true);
1439            checkAndFireSequenceComplete();
1440        }
1441
1442    } // public class CameraDeviceCallbacks
1443
1444    /**
1445     * Default handler management.
1446     *
1447     * <p>
1448     * If handler is null, get the current thread's
1449     * Looper to create a Handler with. If no looper exists, throw {@code IllegalArgumentException}.
1450     * </p>
1451     */
1452    static Handler checkHandler(Handler handler) {
1453        if (handler == null) {
1454            Looper looper = Looper.myLooper();
1455            if (looper == null) {
1456                throw new IllegalArgumentException(
1457                    "No handler given, and current thread has no looper!");
1458            }
1459            handler = new Handler(looper);
1460        }
1461        return handler;
1462    }
1463
1464    /**
1465     * Default handler management, conditional on there being a callback.
1466     *
1467     * <p>If the callback isn't null, check the handler, otherwise pass it through.</p>
1468     */
1469    static <T> Handler checkHandler(Handler handler, T callback) {
1470        if (callback != null) {
1471            return checkHandler(handler);
1472        }
1473        return handler;
1474    }
1475
1476    private void checkIfCameraClosedOrInError() throws CameraAccessException {
1477        if (mInError) {
1478            throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
1479                    "The camera device has encountered a serious error");
1480        }
1481        if (mRemoteDevice == null) {
1482            throw new IllegalStateException("CameraDevice was already closed");
1483        }
1484    }
1485
1486    /** Whether the camera device has started to close (may not yet have finished) */
1487    private boolean isClosed() {
1488        return mClosing;
1489    }
1490
1491    private CameraCharacteristics getCharacteristics() {
1492        return mCharacteristics;
1493    }
1494}
1495