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