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