CameraDeviceImpl.java revision a45aa0d54937495c30a89b445ced8b686940cc08
1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.hardware.camera2.impl;
18
19import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
20
21import android.hardware.camera2.CameraAccessException;
22import android.hardware.camera2.CameraCaptureSession;
23import android.hardware.camera2.CameraCharacteristics;
24import android.hardware.camera2.CameraDevice;
25import android.hardware.camera2.CaptureRequest;
26import android.hardware.camera2.CaptureResult;
27import android.hardware.camera2.CaptureFailure;
28import android.hardware.camera2.ICameraDeviceCallbacks;
29import android.hardware.camera2.ICameraDeviceUser;
30import android.hardware.camera2.TotalCaptureResult;
31import android.hardware.camera2.params.InputConfiguration;
32import android.hardware.camera2.params.OutputConfiguration;
33import android.hardware.camera2.params.ReprocessFormatsMap;
34import android.hardware.camera2.params.StreamConfigurationMap;
35import android.hardware.camera2.utils.CameraBinderDecorator;
36import android.hardware.camera2.utils.CameraRuntimeException;
37import android.hardware.camera2.utils.LongParcelable;
38import android.os.Handler;
39import android.os.IBinder;
40import android.os.Looper;
41import android.os.RemoteException;
42import android.util.Log;
43import android.util.Size;
44import android.util.SparseArray;
45import android.view.Surface;
46
47import java.util.AbstractMap.SimpleEntry;
48import java.util.ArrayList;
49import java.util.HashMap;
50import java.util.HashSet;
51import java.util.Iterator;
52import java.util.List;
53import java.util.LinkedList;
54import java.util.TreeMap;
55
56/**
57 * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
58 */
59public class CameraDeviceImpl extends CameraDevice {
60    private final String TAG;
61    private final boolean DEBUG = false;
62
63    private static final int REQUEST_ID_NONE = -1;
64
65    // TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
66    private ICameraDeviceUser mRemoteDevice;
67
68    // Lock to synchronize cross-thread access to device public interface
69    final Object mInterfaceLock = new Object(); // access from this class and Session only!
70    private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
71
72    private final StateCallback mDeviceCallback;
73    private volatile StateCallbackKK mSessionStateCallback;
74    private final Handler mDeviceHandler;
75
76    private volatile boolean mClosing = false;
77    private boolean mInError = false;
78    private boolean mIdle = true;
79
80    /** map request IDs to callback/request data */
81    private final SparseArray<CaptureCallbackHolder> mCaptureCallbackMap =
82            new SparseArray<CaptureCallbackHolder>();
83
84    private int mRepeatingRequestId = REQUEST_ID_NONE;
85    private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>();
86    // Map stream IDs to input/output configurations
87    private SimpleEntry<Integer, InputConfiguration> mConfiguredInput =
88            new SimpleEntry<>(REQUEST_ID_NONE, null);
89    private final SparseArray<OutputConfiguration> mConfiguredOutputs =
90            new SparseArray<>();
91
92    private final String mCameraId;
93    private final CameraCharacteristics mCharacteristics;
94    private final int mTotalPartialCount;
95
96    /**
97     * A list tracking request and its expected last regular frame number and last reprocess frame
98     * number. Updated when calling ICameraDeviceUser methods.
99     */
100    private final List<RequestLastFrameNumbersHolder> mRequestLastFrameNumbersList =
101            new ArrayList<>();
102
103    /**
104     * An object tracking received frame numbers.
105     * Updated when receiving callbacks from ICameraDeviceCallbacks.
106     */
107    private final FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker();
108
109    private CameraCaptureSessionImpl mCurrentSession;
110    private int mNextSessionId = 0;
111
112    // Runnables for all state transitions, except error, which needs the
113    // error code argument
114
115    private final Runnable mCallOnOpened = new Runnable() {
116        @Override
117        public void run() {
118            StateCallbackKK sessionCallback = null;
119            synchronized(mInterfaceLock) {
120                if (mRemoteDevice == null) return; // Camera already closed
121
122                sessionCallback = mSessionStateCallback;
123            }
124            if (sessionCallback != null) {
125                sessionCallback.onOpened(CameraDeviceImpl.this);
126            }
127            mDeviceCallback.onOpened(CameraDeviceImpl.this);
128        }
129    };
130
131    private final Runnable mCallOnUnconfigured = new Runnable() {
132        @Override
133        public void run() {
134            StateCallbackKK sessionCallback = null;
135            synchronized(mInterfaceLock) {
136                if (mRemoteDevice == null) return; // Camera already closed
137
138                sessionCallback = mSessionStateCallback;
139            }
140            if (sessionCallback != null) {
141                sessionCallback.onUnconfigured(CameraDeviceImpl.this);
142            }
143        }
144    };
145
146    private final Runnable mCallOnActive = new Runnable() {
147        @Override
148        public void run() {
149            StateCallbackKK sessionCallback = null;
150            synchronized(mInterfaceLock) {
151                if (mRemoteDevice == null) return; // Camera already closed
152
153                sessionCallback = mSessionStateCallback;
154            }
155            if (sessionCallback != null) {
156                sessionCallback.onActive(CameraDeviceImpl.this);
157            }
158        }
159    };
160
161    private final Runnable mCallOnBusy = new Runnable() {
162        @Override
163        public void run() {
164            StateCallbackKK sessionCallback = null;
165            synchronized(mInterfaceLock) {
166                if (mRemoteDevice == null) return; // Camera already closed
167
168                sessionCallback = mSessionStateCallback;
169            }
170            if (sessionCallback != null) {
171                sessionCallback.onBusy(CameraDeviceImpl.this);
172            }
173        }
174    };
175
176    private final Runnable mCallOnClosed = new Runnable() {
177        private boolean mClosedOnce = false;
178
179        @Override
180        public void run() {
181            if (mClosedOnce) {
182                throw new AssertionError("Don't post #onClosed more than once");
183            }
184            StateCallbackKK sessionCallback = null;
185            synchronized(mInterfaceLock) {
186                sessionCallback = mSessionStateCallback;
187            }
188            if (sessionCallback != null) {
189                sessionCallback.onClosed(CameraDeviceImpl.this);
190            }
191            mDeviceCallback.onClosed(CameraDeviceImpl.this);
192            mClosedOnce = true;
193        }
194    };
195
196    private final Runnable mCallOnIdle = new Runnable() {
197        @Override
198        public void run() {
199            StateCallbackKK sessionCallback = null;
200            synchronized(mInterfaceLock) {
201                if (mRemoteDevice == null) return; // Camera already closed
202
203                sessionCallback = mSessionStateCallback;
204            }
205            if (sessionCallback != null) {
206                sessionCallback.onIdle(CameraDeviceImpl.this);
207            }
208        }
209    };
210
211    private final Runnable mCallOnDisconnected = new Runnable() {
212        @Override
213        public void run() {
214            StateCallbackKK sessionCallback = null;
215            synchronized(mInterfaceLock) {
216                if (mRemoteDevice == null) return; // Camera already closed
217
218                sessionCallback = mSessionStateCallback;
219            }
220            if (sessionCallback != null) {
221                sessionCallback.onDisconnected(CameraDeviceImpl.this);
222            }
223            mDeviceCallback.onDisconnected(CameraDeviceImpl.this);
224        }
225    };
226
227    public CameraDeviceImpl(String cameraId, StateCallback callback, Handler handler,
228                        CameraCharacteristics characteristics) {
229        if (cameraId == null || callback == null || handler == null || characteristics == null) {
230            throw new IllegalArgumentException("Null argument given");
231        }
232        mCameraId = cameraId;
233        mDeviceCallback = callback;
234        mDeviceHandler = handler;
235        mCharacteristics = characteristics;
236
237        final int MAX_TAG_LEN = 23;
238        String tag = String.format("CameraDevice-JV-%s", mCameraId);
239        if (tag.length() > MAX_TAG_LEN) {
240            tag = tag.substring(0, MAX_TAG_LEN);
241        }
242        TAG = tag;
243
244        Integer partialCount =
245                mCharacteristics.get(CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT);
246        if (partialCount == null) {
247            // 1 means partial result is not supported.
248            mTotalPartialCount = 1;
249        } else {
250            mTotalPartialCount = partialCount;
251        }
252    }
253
254    public CameraDeviceCallbacks getCallbacks() {
255        return mCallbacks;
256    }
257
258    public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
259        synchronized(mInterfaceLock) {
260            // TODO: Move from decorator to direct binder-mediated exceptions
261            // If setRemoteFailure already called, do nothing
262            if (mInError) return;
263
264            mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice);
265
266            mDeviceHandler.post(mCallOnOpened);
267            mDeviceHandler.post(mCallOnUnconfigured);
268        }
269    }
270
271    /**
272     * Call to indicate failed connection to a remote camera device.
273     *
274     * <p>This places the camera device in the error state and informs the callback.
275     * Use in place of setRemoteDevice() when startup fails.</p>
276     */
277    public void setRemoteFailure(final CameraRuntimeException failure) {
278        int failureCode = StateCallback.ERROR_CAMERA_DEVICE;
279        boolean failureIsError = true;
280
281        switch (failure.getReason()) {
282            case CameraAccessException.CAMERA_IN_USE:
283                failureCode = StateCallback.ERROR_CAMERA_IN_USE;
284                break;
285            case CameraAccessException.MAX_CAMERAS_IN_USE:
286                failureCode = StateCallback.ERROR_MAX_CAMERAS_IN_USE;
287                break;
288            case CameraAccessException.CAMERA_DISABLED:
289                failureCode = StateCallback.ERROR_CAMERA_DISABLED;
290                break;
291            case CameraAccessException.CAMERA_DISCONNECTED:
292                failureIsError = false;
293                break;
294            case CameraAccessException.CAMERA_ERROR:
295                failureCode = StateCallback.ERROR_CAMERA_DEVICE;
296                break;
297            default:
298                Log.wtf(TAG, "Unknown failure in opening camera device: " + failure.getReason());
299                break;
300        }
301        final int code = failureCode;
302        final boolean isError = failureIsError;
303        synchronized(mInterfaceLock) {
304            mInError = true;
305            mDeviceHandler.post(new Runnable() {
306                @Override
307                public void run() {
308                    if (isError) {
309                        mDeviceCallback.onError(CameraDeviceImpl.this, code);
310                    } else {
311                        mDeviceCallback.onDisconnected(CameraDeviceImpl.this);
312                    }
313                }
314            });
315        }
316    }
317
318    @Override
319    public String getId() {
320        return mCameraId;
321    }
322
323    public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
324        // Leave this here for backwards compatibility with older code using this directly
325        ArrayList<OutputConfiguration> outputConfigs = new ArrayList<>(outputs.size());
326        for (Surface s : outputs) {
327            outputConfigs.add(new OutputConfiguration(s));
328        }
329        configureStreamsChecked(/*inputConfig*/null, outputConfigs);
330
331    }
332
333    /**
334     * Attempt to configure the input and outputs; the device goes to idle and then configures the
335     * new input and outputs if possible.
336     *
337     * <p>The configuration may gracefully fail, if input configuration is not supported,
338     * if there are too many outputs, if the formats are not supported, or if the sizes for that
339     * format is not supported. In this case this function will return {@code false} and the
340     * unconfigured callback will be fired.</p>
341     *
342     * <p>If the configuration succeeds (with 1 or more outputs with or without an input),
343     * then the idle callback is fired. Unconfiguring the device always fires the idle callback.</p>
344     *
345     * @param inputConfig input configuration or {@code null} for no input
346     * @param outputs a list of one or more surfaces, or {@code null} to unconfigure
347     * @return whether or not the configuration was successful
348     *
349     * @throws CameraAccessException if there were any unexpected problems during configuration
350     */
351    public boolean configureStreamsChecked(InputConfiguration inputConfig,
352            List<OutputConfiguration> outputs) throws CameraAccessException {
353        // Treat a null input the same an empty list
354        if (outputs == null) {
355            outputs = new ArrayList<OutputConfiguration>();
356        }
357        if (outputs.size() == 0 && inputConfig != null) {
358            throw new IllegalArgumentException("cannot configure an input stream without " +
359                    "any output streams");
360        }
361
362        checkInputConfiguration(inputConfig);
363
364        boolean success = false;
365
366        synchronized(mInterfaceLock) {
367            checkIfCameraClosedOrInError();
368            // Streams to create
369            HashSet<OutputConfiguration> addSet = new HashSet<OutputConfiguration>(outputs);
370            // Streams to delete
371            List<Integer> deleteList = new ArrayList<Integer>();
372
373            // Determine which streams need to be created, which to be deleted
374            for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
375                int streamId = mConfiguredOutputs.keyAt(i);
376                OutputConfiguration outConfig = mConfiguredOutputs.valueAt(i);
377
378                if (!outputs.contains(outConfig)) {
379                    deleteList.add(streamId);
380                } else {
381                    addSet.remove(outConfig);  // Don't create a stream previously created
382                }
383            }
384
385            mDeviceHandler.post(mCallOnBusy);
386            stopRepeating();
387
388            try {
389                waitUntilIdle();
390
391                mRemoteDevice.beginConfigure();
392
393                // reconfigure the input stream if the input configuration is different.
394                InputConfiguration currentInputConfig = mConfiguredInput.getValue();
395                if (inputConfig != currentInputConfig &&
396                        (inputConfig == null || !inputConfig.equals(currentInputConfig))) {
397                    if (currentInputConfig != null) {
398                        mRemoteDevice.deleteStream(mConfiguredInput.getKey());
399                        mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>(
400                                REQUEST_ID_NONE, null);
401                    }
402                    if (inputConfig != null) {
403                        int streamId = mRemoteDevice.createInputStream(inputConfig.getWidth(),
404                                inputConfig.getHeight(), inputConfig.getFormat());
405                        mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>(
406                                streamId, inputConfig);
407                    }
408                }
409
410                // Delete all streams first (to free up HW resources)
411                for (Integer streamId : deleteList) {
412                    mRemoteDevice.deleteStream(streamId);
413                    mConfiguredOutputs.delete(streamId);
414                }
415
416                // Add all new streams
417                for (OutputConfiguration outConfig : outputs) {
418                    if (addSet.contains(outConfig)) {
419                        int streamId = mRemoteDevice.createStream(outConfig);
420                        mConfiguredOutputs.put(streamId, outConfig);
421                    }
422                }
423
424                try {
425                    mRemoteDevice.endConfigure();
426                }
427                catch (IllegalArgumentException e) {
428                    // OK. camera service can reject stream config if it's not supported by HAL
429                    // This is only the result of a programmer misusing the camera2 api.
430                    Log.w(TAG, "Stream configuration failed");
431                    return false;
432                }
433
434                success = true;
435            } catch (CameraRuntimeException e) {
436                if (e.getReason() == CAMERA_IN_USE) {
437                    throw new IllegalStateException("The camera is currently busy." +
438                            " You must wait until the previous operation completes.");
439                }
440
441                throw e.asChecked();
442            } catch (RemoteException e) {
443                // impossible
444                return false;
445            } finally {
446                if (success && outputs.size() > 0) {
447                    mDeviceHandler.post(mCallOnIdle);
448                } else {
449                    // Always return to the 'unconfigured' state if we didn't hit a fatal error
450                    mDeviceHandler.post(mCallOnUnconfigured);
451                }
452            }
453        }
454
455        return success;
456    }
457
458    @Override
459    public void createCaptureSession(List<Surface> outputs,
460            CameraCaptureSession.StateCallback callback, Handler handler)
461            throws CameraAccessException {
462        List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size());
463        for (Surface surface : outputs) {
464            outConfigurations.add(new OutputConfiguration(surface));
465        }
466        createCaptureSessionInternal(null, outConfigurations, callback, handler);
467    }
468
469    @Override
470    public void createCaptureSessionByOutputConfiguration(
471            List<OutputConfiguration> outputConfigurations,
472            CameraCaptureSession.StateCallback callback, Handler handler)
473            throws CameraAccessException {
474        if (DEBUG) {
475            Log.d(TAG, "createCaptureSessionByOutputConfiguration");
476        }
477
478        createCaptureSessionInternal(null, outputConfigurations, callback, handler);
479    }
480
481    @Override
482    public void createReprocessableCaptureSession(InputConfiguration inputConfig,
483            List<Surface> outputs, CameraCaptureSession.StateCallback callback, Handler handler)
484            throws CameraAccessException {
485        if (DEBUG) {
486            Log.d(TAG, "createReprocessableCaptureSession");
487        }
488
489        if (inputConfig == null) {
490            throw new IllegalArgumentException("inputConfig cannot be null when creating a " +
491                    "reprocessable capture session");
492        }
493        List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size());
494        for (Surface surface : outputs) {
495            outConfigurations.add(new OutputConfiguration(surface));
496        }
497        createCaptureSessionInternal(inputConfig, outConfigurations, callback, handler);
498    }
499
500    private void createCaptureSessionInternal(InputConfiguration inputConfig,
501            List<OutputConfiguration> outputConfigurations,
502            CameraCaptureSession.StateCallback callback, Handler handler)
503            throws CameraAccessException {
504        synchronized(mInterfaceLock) {
505            if (DEBUG) {
506                Log.d(TAG, "createCaptureSessionInternal");
507            }
508
509            checkIfCameraClosedOrInError();
510
511            // Notify current session that it's going away, before starting camera operations
512            // After this call completes, the session is not allowed to call into CameraDeviceImpl
513            if (mCurrentSession != null) {
514                mCurrentSession.replaceSessionClose();
515            }
516
517            // TODO: dont block for this
518            boolean configureSuccess = true;
519            CameraAccessException pendingException = null;
520            Surface input = null;
521            try {
522                 // configure streams and then block until IDLE
523                configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations);
524                if (inputConfig != null) {
525                    input = new Surface();
526                    mRemoteDevice.getInputSurface(/*out*/input);
527                }
528            } catch (CameraAccessException e) {
529                configureSuccess = false;
530                pendingException = e;
531                input = null;
532                if (DEBUG) {
533                    Log.v(TAG, "createCaptureSession - failed with exception ", e);
534                }
535            } catch (RemoteException e) {
536                // impossible
537                return;
538            }
539
540            List<Surface> outSurfaces = new ArrayList<>(outputConfigurations.size());
541            for (OutputConfiguration config : outputConfigurations) {
542                outSurfaces.add(config.getSurface());
543            }
544            // Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise.
545            CameraCaptureSessionImpl newSession =
546                    new CameraCaptureSessionImpl(mNextSessionId++, input,
547                            outSurfaces, callback, handler, this, mDeviceHandler,
548                            configureSuccess);
549
550            // TODO: wait until current session closes, then create the new session
551            mCurrentSession = newSession;
552
553            if (pendingException != null) {
554                throw pendingException;
555            }
556
557            mSessionStateCallback = mCurrentSession.getDeviceStateCallback();
558        }
559    }
560
561    /**
562     * For use by backwards-compatibility code only.
563     */
564    public void setSessionListener(StateCallbackKK sessionCallback) {
565        synchronized(mInterfaceLock) {
566            mSessionStateCallback = sessionCallback;
567        }
568    }
569
570    @Override
571    public CaptureRequest.Builder createCaptureRequest(int templateType)
572            throws CameraAccessException {
573        synchronized(mInterfaceLock) {
574            checkIfCameraClosedOrInError();
575
576            CameraMetadataNative templatedRequest = new CameraMetadataNative();
577
578            try {
579                mRemoteDevice.createDefaultRequest(templateType, /*out*/templatedRequest);
580            } catch (CameraRuntimeException e) {
581                throw e.asChecked();
582            } catch (RemoteException e) {
583                // impossible
584                return null;
585            }
586
587            CaptureRequest.Builder builder = new CaptureRequest.Builder(
588                    templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
589
590            return builder;
591        }
592    }
593
594    @Override
595    public CaptureRequest.Builder createReprocessCaptureRequest(TotalCaptureResult inputResult)
596            throws CameraAccessException {
597        synchronized(mInterfaceLock) {
598            checkIfCameraClosedOrInError();
599
600            CameraMetadataNative resultMetadata = new
601                    CameraMetadataNative(inputResult.getNativeCopy());
602
603            return new CaptureRequest.Builder(resultMetadata, /*reprocess*/true,
604                    inputResult.getSessionId());
605        }
606    }
607
608    public void prepare(Surface surface) throws CameraAccessException {
609        if (surface == null) throw new IllegalArgumentException("Surface is null");
610
611        synchronized(mInterfaceLock) {
612            int streamId = -1;
613            for (int i = 0; i < mConfiguredOutputs.size(); i++) {
614                if (surface == mConfiguredOutputs.valueAt(i).getSurface()) {
615                    streamId = mConfiguredOutputs.keyAt(i);
616                    break;
617                }
618            }
619            if (streamId == -1) {
620                throw new IllegalArgumentException("Surface is not part of this session");
621            }
622            try {
623                mRemoteDevice.prepare(streamId);
624            } catch (CameraRuntimeException e) {
625                throw e.asChecked();
626            } catch (RemoteException e) {
627                // impossible
628                return;
629            }
630        }
631    }
632
633    public int capture(CaptureRequest request, CaptureCallback callback, Handler handler)
634            throws CameraAccessException {
635        if (DEBUG) {
636            Log.d(TAG, "calling capture");
637        }
638        List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
639        requestList.add(request);
640        return submitCaptureRequest(requestList, callback, handler, /*streaming*/false);
641    }
642
643    public int captureBurst(List<CaptureRequest> requests, CaptureCallback callback,
644            Handler handler) throws CameraAccessException {
645        if (requests == null || requests.isEmpty()) {
646            throw new IllegalArgumentException("At least one request must be given");
647        }
648        return submitCaptureRequest(requests, callback, handler, /*streaming*/false);
649    }
650
651    /**
652     * This method checks lastFrameNumber returned from ICameraDeviceUser methods for
653     * starting and stopping repeating request and flushing.
654     *
655     * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never
656     * sent to HAL. Then onCaptureSequenceAborted is immediately triggered.
657     * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber as the last
658     * regular frame number will be added to the list mRequestLastFrameNumbersList.</p>
659     *
660     * @param requestId the request ID of the current repeating request.
661     *
662     * @param lastFrameNumber last frame number returned from binder.
663     */
664    private void checkEarlyTriggerSequenceComplete(
665            final int requestId, final long lastFrameNumber) {
666        // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request
667        // was never sent to HAL. Should trigger onCaptureSequenceAborted immediately.
668        if (lastFrameNumber == CaptureCallback.NO_FRAMES_CAPTURED) {
669            final CaptureCallbackHolder holder;
670            int index = mCaptureCallbackMap.indexOfKey(requestId);
671            holder = (index >= 0) ? mCaptureCallbackMap.valueAt(index) : null;
672            if (holder != null) {
673                mCaptureCallbackMap.removeAt(index);
674                if (DEBUG) {
675                    Log.v(TAG, String.format(
676                            "remove holder for requestId %d, "
677                            + "because lastFrame is %d.",
678                            requestId, lastFrameNumber));
679                }
680            }
681
682            if (holder != null) {
683                if (DEBUG) {
684                    Log.v(TAG, "immediately trigger onCaptureSequenceAborted because"
685                            + " request did not reach HAL");
686                }
687
688                Runnable resultDispatch = new Runnable() {
689                    @Override
690                    public void run() {
691                        if (!CameraDeviceImpl.this.isClosed()) {
692                            if (DEBUG) {
693                                Log.d(TAG, String.format(
694                                        "early trigger sequence complete for request %d",
695                                        requestId));
696                            }
697                            holder.getCallback().onCaptureSequenceAborted(
698                                    CameraDeviceImpl.this,
699                                    requestId);
700                        }
701                    }
702                };
703                holder.getHandler().post(resultDispatch);
704            } else {
705                Log.w(TAG, String.format(
706                        "did not register callback to request %d",
707                        requestId));
708            }
709        } else {
710            // This function is only called for regular request so lastFrameNumber is the last
711            // regular frame number.
712            mRequestLastFrameNumbersList.add(new RequestLastFrameNumbersHolder(requestId,
713                    lastFrameNumber));
714
715            // It is possible that the last frame has already arrived, so we need to check
716            // for sequence completion right away
717            checkAndFireSequenceComplete();
718        }
719    }
720
721    private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback,
722            Handler handler, boolean repeating) throws CameraAccessException {
723
724        // Need a valid handler, or current thread needs to have a looper, if
725        // callback is valid
726        handler = checkHandler(handler, callback);
727
728        // Make sure that there all requests have at least 1 surface; all surfaces are non-null
729        for (CaptureRequest request : requestList) {
730            if (request.getTargets().isEmpty()) {
731                throw new IllegalArgumentException(
732                        "Each request must have at least one Surface target");
733            }
734
735            for (Surface surface : request.getTargets()) {
736                if (surface == null) {
737                    throw new IllegalArgumentException("Null Surface targets are not allowed");
738                }
739            }
740        }
741
742        synchronized(mInterfaceLock) {
743            checkIfCameraClosedOrInError();
744            int requestId;
745
746            if (repeating) {
747                stopRepeating();
748            }
749
750            LongParcelable lastFrameNumberRef = new LongParcelable();
751            try {
752                requestId = mRemoteDevice.submitRequestList(requestList, repeating,
753                        /*out*/lastFrameNumberRef);
754                if (DEBUG) {
755                    Log.v(TAG, "last frame number " + lastFrameNumberRef.getNumber());
756                }
757            } catch (CameraRuntimeException e) {
758                throw e.asChecked();
759            } catch (RemoteException e) {
760                // impossible
761                return -1;
762            }
763
764            if (callback != null) {
765                mCaptureCallbackMap.put(requestId, new CaptureCallbackHolder(callback,
766                        requestList, handler, repeating, mNextSessionId - 1));
767            } else {
768                if (DEBUG) {
769                    Log.d(TAG, "Listen for request " + requestId + " is null");
770                }
771            }
772
773            long lastFrameNumber = lastFrameNumberRef.getNumber();
774
775            if (repeating) {
776                if (mRepeatingRequestId != REQUEST_ID_NONE) {
777                    checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
778                }
779                mRepeatingRequestId = requestId;
780            } else {
781                mRequestLastFrameNumbersList.add(new RequestLastFrameNumbersHolder(requestList,
782                        requestId, lastFrameNumber));
783            }
784
785            if (mIdle) {
786                mDeviceHandler.post(mCallOnActive);
787            }
788            mIdle = false;
789
790            return requestId;
791        }
792    }
793
794    public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback,
795            Handler handler) throws CameraAccessException {
796        List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
797        requestList.add(request);
798        return submitCaptureRequest(requestList, callback, handler, /*streaming*/true);
799    }
800
801    public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback callback,
802            Handler handler) throws CameraAccessException {
803        if (requests == null || requests.isEmpty()) {
804            throw new IllegalArgumentException("At least one request must be given");
805        }
806        return submitCaptureRequest(requests, callback, handler, /*streaming*/true);
807    }
808
809    public void stopRepeating() throws CameraAccessException {
810
811        synchronized(mInterfaceLock) {
812            checkIfCameraClosedOrInError();
813            if (mRepeatingRequestId != REQUEST_ID_NONE) {
814
815                int requestId = mRepeatingRequestId;
816                mRepeatingRequestId = REQUEST_ID_NONE;
817
818                // Queue for deletion after in-flight requests finish
819                if (mCaptureCallbackMap.get(requestId) != null) {
820                    mRepeatingRequestIdDeletedList.add(requestId);
821                }
822
823                try {
824                    LongParcelable lastFrameNumberRef = new LongParcelable();
825                    mRemoteDevice.cancelRequest(requestId, /*out*/lastFrameNumberRef);
826                    long lastFrameNumber = lastFrameNumberRef.getNumber();
827
828                    checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber);
829
830                } catch (CameraRuntimeException e) {
831                    throw e.asChecked();
832                } catch (RemoteException e) {
833                    // impossible
834                    return;
835                }
836            }
837        }
838    }
839
840    private void waitUntilIdle() throws CameraAccessException {
841
842        synchronized(mInterfaceLock) {
843            checkIfCameraClosedOrInError();
844
845            if (mRepeatingRequestId != REQUEST_ID_NONE) {
846                throw new IllegalStateException("Active repeating request ongoing");
847            }
848            try {
849                mRemoteDevice.waitUntilIdle();
850            } catch (CameraRuntimeException e) {
851                throw e.asChecked();
852            } catch (RemoteException e) {
853                // impossible
854                return;
855            }
856        }
857    }
858
859    public void flush() throws CameraAccessException {
860        synchronized(mInterfaceLock) {
861            checkIfCameraClosedOrInError();
862
863            mDeviceHandler.post(mCallOnBusy);
864
865            // If already idle, just do a busy->idle transition immediately, don't actually
866            // flush.
867            if (mIdle) {
868                mDeviceHandler.post(mCallOnIdle);
869                return;
870            }
871            try {
872                LongParcelable lastFrameNumberRef = new LongParcelable();
873                mRemoteDevice.flush(/*out*/lastFrameNumberRef);
874                if (mRepeatingRequestId != REQUEST_ID_NONE) {
875                    long lastFrameNumber = lastFrameNumberRef.getNumber();
876                    checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
877                    mRepeatingRequestId = REQUEST_ID_NONE;
878                }
879            } catch (CameraRuntimeException e) {
880                throw e.asChecked();
881            } catch (RemoteException e) {
882                // impossible
883                return;
884            }
885        }
886    }
887
888    @Override
889    public void close() {
890        synchronized (mInterfaceLock) {
891            try {
892                if (mRemoteDevice != null) {
893                    mRemoteDevice.disconnect();
894                }
895            } catch (CameraRuntimeException e) {
896                Log.e(TAG, "Exception while closing: ", e.asChecked());
897            } catch (RemoteException e) {
898                // impossible
899            }
900
901            // Only want to fire the onClosed callback once;
902            // either a normal close where the remote device is valid
903            // or a close after a startup error (no remote device but in error state)
904            if (mRemoteDevice != null || mInError) {
905                mDeviceHandler.post(mCallOnClosed);
906            }
907
908            mRemoteDevice = null;
909        }
910    }
911
912    @Override
913    protected void finalize() throws Throwable {
914        try {
915            close();
916        }
917        finally {
918            super.finalize();
919        }
920    }
921
922    private void checkInputConfiguration(InputConfiguration inputConfig) {
923        if (inputConfig != null) {
924            StreamConfigurationMap configMap = mCharacteristics.get(
925                    CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
926
927            int[] inputFormats = configMap.getInputFormats();
928            boolean validFormat = false;
929            for (int format : inputFormats) {
930                if (format == inputConfig.getFormat()) {
931                    validFormat = true;
932                }
933            }
934
935            if (validFormat == false) {
936                throw new IllegalArgumentException("input format " + inputConfig.getFormat() +
937                        " is not valid");
938            }
939
940            boolean validSize = false;
941            Size[] inputSizes = configMap.getInputSizes(inputConfig.getFormat());
942            for (Size s : inputSizes) {
943                if (inputConfig.getWidth() == s.getWidth() &&
944                        inputConfig.getHeight() == s.getHeight()) {
945                    validSize = true;
946                }
947            }
948
949            if (validSize == false) {
950                throw new IllegalArgumentException("input size " + inputConfig.getWidth() + "x" +
951                        inputConfig.getHeight() + " is not valid");
952            }
953        }
954    }
955
956    /**
957     * <p>A callback for tracking the progress of a {@link CaptureRequest}
958     * submitted to the camera device.</p>
959     *
960     */
961    public static abstract class CaptureCallback {
962
963        /**
964         * This constant is used to indicate that no images were captured for
965         * the request.
966         *
967         * @hide
968         */
969        public static final int NO_FRAMES_CAPTURED = -1;
970
971        /**
972         * This method is called when the camera device has started capturing
973         * the output image for the request, at the beginning of image exposure.
974         *
975         * @see android.media.MediaActionSound
976         */
977        public void onCaptureStarted(CameraDevice camera,
978                CaptureRequest request, long timestamp, long frameNumber) {
979            // default empty implementation
980        }
981
982        /**
983         * This method is called when some results from an image capture are
984         * available.
985         *
986         * @hide
987         */
988        public void onCapturePartial(CameraDevice camera,
989                CaptureRequest request, CaptureResult result) {
990            // default empty implementation
991        }
992
993        /**
994         * This method is called when an image capture makes partial forward progress; some
995         * (but not all) results from an image capture are available.
996         *
997         */
998        public void onCaptureProgressed(CameraDevice camera,
999                CaptureRequest request, CaptureResult partialResult) {
1000            // default empty implementation
1001        }
1002
1003        /**
1004         * This method is called when an image capture has fully completed and all the
1005         * result metadata is available.
1006         */
1007        public void onCaptureCompleted(CameraDevice camera,
1008                CaptureRequest request, TotalCaptureResult result) {
1009            // default empty implementation
1010        }
1011
1012        /**
1013         * This method is called instead of {@link #onCaptureCompleted} when the
1014         * camera device failed to produce a {@link CaptureResult} for the
1015         * request.
1016         */
1017        public void onCaptureFailed(CameraDevice camera,
1018                CaptureRequest request, CaptureFailure failure) {
1019            // default empty implementation
1020        }
1021
1022        /**
1023         * This method is called independently of the others in CaptureCallback,
1024         * when a capture sequence finishes and all {@link CaptureResult}
1025         * or {@link CaptureFailure} for it have been returned via this callback.
1026         */
1027        public void onCaptureSequenceCompleted(CameraDevice camera,
1028                int sequenceId, long frameNumber) {
1029            // default empty implementation
1030        }
1031
1032        /**
1033         * This method is called independently of the others in CaptureCallback,
1034         * when a capture sequence aborts before any {@link CaptureResult}
1035         * or {@link CaptureFailure} for it have been returned via this callback.
1036         */
1037        public void onCaptureSequenceAborted(CameraDevice camera,
1038                int sequenceId) {
1039            // default empty implementation
1040        }
1041    }
1042
1043    /**
1044     * A callback for notifications about the state of a camera device, adding in the callbacks that
1045     * were part of the earlier KK API design, but now only used internally.
1046     */
1047    public static abstract class StateCallbackKK extends StateCallback {
1048        /**
1049         * The method called when a camera device has no outputs configured.
1050         *
1051         */
1052        public void onUnconfigured(CameraDevice camera) {
1053            // Default empty implementation
1054        }
1055
1056        /**
1057         * The method called when a camera device begins processing
1058         * {@link CaptureRequest capture requests}.
1059         *
1060         */
1061        public void onActive(CameraDevice camera) {
1062            // Default empty implementation
1063        }
1064
1065        /**
1066         * The method called when a camera device is busy.
1067         *
1068         */
1069        public void onBusy(CameraDevice camera) {
1070            // Default empty implementation
1071        }
1072
1073        /**
1074         * The method called when a camera device has finished processing all
1075         * submitted capture requests and has reached an idle state.
1076         *
1077         */
1078        public void onIdle(CameraDevice camera) {
1079            // Default empty implementation
1080        }
1081
1082        /**
1083         * The method called when the camera device has finished preparing
1084         * an output Surface
1085         */
1086        public void onSurfacePrepared(Surface surface) {
1087            // Default empty implementation
1088        }
1089    }
1090
1091    static class CaptureCallbackHolder {
1092
1093        private final boolean mRepeating;
1094        private final CaptureCallback mCallback;
1095        private final List<CaptureRequest> mRequestList;
1096        private final Handler mHandler;
1097        private final int mSessionId;
1098
1099        CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList,
1100                Handler handler, boolean repeating, int sessionId) {
1101            if (callback == null || handler == null) {
1102                throw new UnsupportedOperationException(
1103                    "Must have a valid handler and a valid callback");
1104            }
1105            mRepeating = repeating;
1106            mHandler = handler;
1107            mRequestList = new ArrayList<CaptureRequest>(requestList);
1108            mCallback = callback;
1109            mSessionId = sessionId;
1110        }
1111
1112        public boolean isRepeating() {
1113            return mRepeating;
1114        }
1115
1116        public CaptureCallback getCallback() {
1117            return mCallback;
1118        }
1119
1120        public CaptureRequest getRequest(int subsequenceId) {
1121            if (subsequenceId >= mRequestList.size()) {
1122                throw new IllegalArgumentException(
1123                        String.format(
1124                                "Requested subsequenceId %d is larger than request list size %d.",
1125                                subsequenceId, mRequestList.size()));
1126            } else {
1127                if (subsequenceId < 0) {
1128                    throw new IllegalArgumentException(String.format(
1129                            "Requested subsequenceId %d is negative", subsequenceId));
1130                } else {
1131                    return mRequestList.get(subsequenceId);
1132                }
1133            }
1134        }
1135
1136        public CaptureRequest getRequest() {
1137            return getRequest(0);
1138        }
1139
1140        public Handler getHandler() {
1141            return mHandler;
1142        }
1143
1144        public int getSessionId() {
1145            return mSessionId;
1146        }
1147    }
1148
1149    /**
1150     * This class holds a capture ID and its expected last regular frame number and last reprocess
1151     * frame number.
1152     */
1153    static class RequestLastFrameNumbersHolder {
1154        // request ID
1155        private final int mRequestId;
1156        // The last regular frame number for this request ID. It's
1157        // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no regular request.
1158        private final long mLastRegularFrameNumber;
1159        // The last reprocess frame number for this request ID. It's
1160        // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no reprocess request.
1161        private final long mLastReprocessFrameNumber;
1162
1163        /**
1164         * Create a request-last-frame-numbers holder with a list of requests, request ID, and
1165         * the last frame number returned by camera service.
1166         */
1167        public RequestLastFrameNumbersHolder(List<CaptureRequest> requestList, int requestId,
1168                long lastFrameNumber) {
1169            long lastRegularFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
1170            long lastReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
1171            long frameNumber = lastFrameNumber;
1172
1173            if (lastFrameNumber < requestList.size() - 1) {
1174                throw new IllegalArgumentException("lastFrameNumber: " + lastFrameNumber +
1175                        " should be at least " + (requestList.size() - 1) + " for the number of " +
1176                        " requests in the list: " + requestList.size());
1177            }
1178
1179            // find the last regular frame number and the last reprocess frame number
1180            for (int i = requestList.size() - 1; i >= 0; i--) {
1181                CaptureRequest request = requestList.get(i);
1182                if (request.isReprocess() && lastReprocessFrameNumber ==
1183                        CaptureCallback.NO_FRAMES_CAPTURED) {
1184                    lastReprocessFrameNumber = frameNumber;
1185                } else if (!request.isReprocess() && lastRegularFrameNumber ==
1186                        CaptureCallback.NO_FRAMES_CAPTURED) {
1187                    lastRegularFrameNumber = frameNumber;
1188                }
1189
1190                if (lastReprocessFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED &&
1191                        lastRegularFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED) {
1192                    break;
1193                }
1194
1195                frameNumber--;
1196            }
1197
1198            mLastRegularFrameNumber = lastRegularFrameNumber;
1199            mLastReprocessFrameNumber = lastReprocessFrameNumber;
1200            mRequestId = requestId;
1201        }
1202
1203        /**
1204         * Create a request-last-frame-numbers holder with a request ID and last regular frame
1205         * number.
1206         */
1207        public RequestLastFrameNumbersHolder(int requestId, long lastRegularFrameNumber) {
1208            mLastRegularFrameNumber = lastRegularFrameNumber;
1209            mLastReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
1210            mRequestId = requestId;
1211        }
1212
1213        /**
1214         * Return the last regular frame number. Return CaptureCallback.NO_FRAMES_CAPTURED if
1215         * it contains no regular request.
1216         */
1217        public long getLastRegularFrameNumber() {
1218            return mLastRegularFrameNumber;
1219        }
1220
1221        /**
1222         * Return the last reprocess frame number. Return CaptureCallback.NO_FRAMES_CAPTURED if
1223         * it contains no reprocess request.
1224         */
1225        public long getLastReprocessFrameNumber() {
1226            return mLastReprocessFrameNumber;
1227        }
1228
1229        /**
1230         * Return the last frame number overall.
1231         */
1232        public long getLastFrameNumber() {
1233            return Math.max(mLastRegularFrameNumber, mLastReprocessFrameNumber);
1234        }
1235
1236        /**
1237         * Return the request ID.
1238         */
1239        public int getRequestId() {
1240            return mRequestId;
1241        }
1242    }
1243
1244    /**
1245     * This class tracks the last frame number for submitted requests.
1246     */
1247    public class FrameNumberTracker {
1248
1249        private long mCompletedFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
1250        private long mCompletedReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
1251        /** the skipped frame numbers that belong to regular results */
1252        private final LinkedList<Long> mSkippedRegularFrameNumbers = new LinkedList<Long>();
1253        /** the skipped frame numbers that belong to reprocess results */
1254        private final LinkedList<Long> mSkippedReprocessFrameNumbers = new LinkedList<Long>();
1255        /** frame number -> is reprocess */
1256        private final TreeMap<Long, Boolean> mFutureErrorMap = new TreeMap<Long, Boolean>();
1257        /** Map frame numbers to list of partial results */
1258        private final HashMap<Long, List<CaptureResult>> mPartialResults = new HashMap<>();
1259
1260        private void update() {
1261            Iterator iter = mFutureErrorMap.entrySet().iterator();
1262            while (iter.hasNext()) {
1263                TreeMap.Entry pair = (TreeMap.Entry)iter.next();
1264                Long errorFrameNumber = (Long)pair.getKey();
1265                Boolean reprocess = (Boolean)pair.getValue();
1266                Boolean removeError = true;
1267                if (reprocess) {
1268                    if (errorFrameNumber == mCompletedReprocessFrameNumber + 1) {
1269                        mCompletedReprocessFrameNumber = errorFrameNumber;
1270                    } else if (mSkippedReprocessFrameNumbers.isEmpty() != true &&
1271                            errorFrameNumber == mSkippedReprocessFrameNumbers.element()) {
1272                        mCompletedReprocessFrameNumber = errorFrameNumber;
1273                        mSkippedReprocessFrameNumbers.remove();
1274                    } else {
1275                        removeError = false;
1276                    }
1277                } else {
1278                    if (errorFrameNumber == mCompletedFrameNumber + 1) {
1279                        mCompletedFrameNumber = errorFrameNumber;
1280                    } else if (mSkippedRegularFrameNumbers.isEmpty() != true &&
1281                            errorFrameNumber == mSkippedRegularFrameNumbers.element()) {
1282                        mCompletedFrameNumber = errorFrameNumber;
1283                        mSkippedRegularFrameNumbers.remove();
1284                    } else {
1285                        removeError = false;
1286                    }
1287                }
1288                if (removeError) {
1289                    iter.remove();
1290                }
1291            }
1292        }
1293
1294        /**
1295         * This function is called every time when a result or an error is received.
1296         * @param frameNumber the frame number corresponding to the result or error
1297         * @param isError true if it is an error, false if it is not an error
1298         * @param isReprocess true if it is a reprocess result, false if it is a regular result.
1299         */
1300        public void updateTracker(long frameNumber, boolean isError, boolean isReprocess) {
1301            if (isError) {
1302                mFutureErrorMap.put(frameNumber, isReprocess);
1303            } else {
1304                try {
1305                    if (isReprocess) {
1306                        updateCompletedReprocessFrameNumber(frameNumber);
1307                    } else {
1308                        updateCompletedFrameNumber(frameNumber);
1309                    }
1310                } catch (IllegalArgumentException e) {
1311                    Log.e(TAG, e.getMessage());
1312                }
1313            }
1314            update();
1315        }
1316
1317        /**
1318         * This function is called every time a result has been completed.
1319         *
1320         * <p>It keeps a track of all the partial results already created for a particular
1321         * frame number.</p>
1322         *
1323         * @param frameNumber the frame number corresponding to the result
1324         * @param result the total or partial result
1325         * @param partial {@true} if the result is partial, {@code false} if total
1326         * @param isReprocess true if it is a reprocess result, false if it is a regular result.
1327         */
1328        public void updateTracker(long frameNumber, CaptureResult result, boolean partial,
1329                boolean isReprocess) {
1330            if (!partial) {
1331                // Update the total result's frame status as being successful
1332                updateTracker(frameNumber, /*isError*/false, isReprocess);
1333                // Don't keep a list of total results, we don't need to track them
1334                return;
1335            }
1336
1337            if (result == null) {
1338                // Do not record blank results; this also means there will be no total result
1339                // so it doesn't matter that the partials were not recorded
1340                return;
1341            }
1342
1343            // Partial results must be aggregated in-order for that frame number
1344            List<CaptureResult> partials = mPartialResults.get(frameNumber);
1345            if (partials == null) {
1346                partials = new ArrayList<>();
1347                mPartialResults.put(frameNumber, partials);
1348            }
1349
1350            partials.add(result);
1351        }
1352
1353        /**
1354         * Attempt to pop off all of the partial results seen so far for the {@code frameNumber}.
1355         *
1356         * <p>Once popped-off, the partial results are forgotten (unless {@code updateTracker}
1357         * is called again with new partials for that frame number).</p>
1358         *
1359         * @param frameNumber the frame number corresponding to the result
1360         * @return a list of partial results for that frame with at least 1 element,
1361         *         or {@code null} if there were no partials recorded for that frame
1362         */
1363        public List<CaptureResult> popPartialResults(long frameNumber) {
1364            return mPartialResults.remove(frameNumber);
1365        }
1366
1367        public long getCompletedFrameNumber() {
1368            return mCompletedFrameNumber;
1369        }
1370
1371        public long getCompletedReprocessFrameNumber() {
1372            return mCompletedReprocessFrameNumber;
1373        }
1374
1375        /**
1376         * Update the completed frame number for regular results.
1377         *
1378         * It validates that all previous frames have arrived except for reprocess frames.
1379         *
1380         * If there is a gap since previous regular frame number, assume the frames in the gap are
1381         * reprocess frames and store them in the skipped reprocess frame number queue to check
1382         * against when reprocess frames arrive.
1383         */
1384        private void updateCompletedFrameNumber(long frameNumber) throws IllegalArgumentException {
1385            if (frameNumber <= mCompletedFrameNumber) {
1386                throw new IllegalArgumentException("frame number " + frameNumber + " is a repeat");
1387            } else if (frameNumber <= mCompletedReprocessFrameNumber) {
1388                // if frame number is smaller than completed reprocess frame number,
1389                // it must be the head of mSkippedRegularFrameNumbers
1390                if (mSkippedRegularFrameNumbers.isEmpty() == true ||
1391                        frameNumber < mSkippedRegularFrameNumbers.element()) {
1392                    throw new IllegalArgumentException("frame number " + frameNumber +
1393                            " is a repeat");
1394                } else if (frameNumber > mSkippedRegularFrameNumbers.element()) {
1395                    throw new IllegalArgumentException("frame number " + frameNumber +
1396                            " comes out of order. Expecting " +
1397                            mSkippedRegularFrameNumbers.element());
1398                }
1399                // frame number matches the head of the skipped frame number queue.
1400                mSkippedRegularFrameNumbers.remove();
1401            } else {
1402                // there is a gap of unseen frame numbers which should belong to reprocess result
1403                // put all the skipped frame numbers in the queue
1404                for (long i = Math.max(mCompletedFrameNumber, mCompletedReprocessFrameNumber) + 1;
1405                        i < frameNumber; i++) {
1406                    mSkippedReprocessFrameNumbers.add(i);
1407                }
1408            }
1409
1410            mCompletedFrameNumber = frameNumber;
1411        }
1412
1413        /**
1414         * Update the completed frame number for reprocess results.
1415         *
1416         * It validates that all previous frames have arrived except for regular frames.
1417         *
1418         * If there is a gap since previous reprocess frame number, assume the frames in the gap are
1419         * regular frames and store them in the skipped regular frame number queue to check
1420         * against when regular frames arrive.
1421         */
1422        private void updateCompletedReprocessFrameNumber(long frameNumber)
1423                throws IllegalArgumentException {
1424            if (frameNumber < mCompletedReprocessFrameNumber) {
1425                throw new IllegalArgumentException("frame number " + frameNumber + " is a repeat");
1426            } else if (frameNumber < mCompletedFrameNumber) {
1427                // if reprocess frame number is smaller than completed regular frame number,
1428                // it must be the head of the skipped reprocess frame number queue.
1429                if (mSkippedReprocessFrameNumbers.isEmpty() == true ||
1430                        frameNumber < mSkippedReprocessFrameNumbers.element()) {
1431                    throw new IllegalArgumentException("frame number " + frameNumber +
1432                            " is a repeat");
1433                } else if (frameNumber > mSkippedReprocessFrameNumbers.element()) {
1434                    throw new IllegalArgumentException("frame number " + frameNumber +
1435                            " comes out of order. Expecting " +
1436                            mSkippedReprocessFrameNumbers.element());
1437                }
1438                // frame number matches the head of the skipped frame number queue.
1439                mSkippedReprocessFrameNumbers.remove();
1440            } else {
1441                // put all the skipped frame numbers in the queue
1442                for (long i = Math.max(mCompletedFrameNumber, mCompletedReprocessFrameNumber) + 1;
1443                        i < frameNumber; i++) {
1444                    mSkippedRegularFrameNumbers.add(i);
1445                }
1446            }
1447            mCompletedReprocessFrameNumber = frameNumber;
1448        }
1449    }
1450
1451    private void checkAndFireSequenceComplete() {
1452        long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
1453        long completedReprocessFrameNumber = mFrameNumberTracker.getCompletedReprocessFrameNumber();
1454        boolean isReprocess = false;
1455        Iterator<RequestLastFrameNumbersHolder> iter = mRequestLastFrameNumbersList.iterator();
1456        while (iter.hasNext()) {
1457            final RequestLastFrameNumbersHolder requestLastFrameNumbers = iter.next();
1458            boolean sequenceCompleted = false;
1459            final int requestId = requestLastFrameNumbers.getRequestId();
1460            final CaptureCallbackHolder holder;
1461            synchronized(mInterfaceLock) {
1462                if (mRemoteDevice == null) {
1463                    Log.w(TAG, "Camera closed while checking sequences");
1464                    return;
1465                }
1466
1467                int index = mCaptureCallbackMap.indexOfKey(requestId);
1468                holder = (index >= 0) ?
1469                        mCaptureCallbackMap.valueAt(index) : null;
1470                if (holder != null) {
1471                    long lastRegularFrameNumber =
1472                            requestLastFrameNumbers.getLastRegularFrameNumber();
1473                    long lastReprocessFrameNumber =
1474                            requestLastFrameNumbers.getLastReprocessFrameNumber();
1475
1476                    // check if it's okay to remove request from mCaptureCallbackMap
1477                    if (lastRegularFrameNumber <= completedFrameNumber &&
1478                            lastReprocessFrameNumber <= completedReprocessFrameNumber) {
1479                        sequenceCompleted = true;
1480                        mCaptureCallbackMap.removeAt(index);
1481                        if (DEBUG) {
1482                            Log.v(TAG, String.format(
1483                                    "Remove holder for requestId %d, because lastRegularFrame %d " +
1484                                    "is <= %d and lastReprocessFrame %d is <= %d", requestId,
1485                                    lastRegularFrameNumber, completedFrameNumber,
1486                                    lastReprocessFrameNumber, completedReprocessFrameNumber));
1487                        }
1488                    }
1489                }
1490            }
1491
1492            // If no callback is registered for this requestId or sequence completed, remove it
1493            // from the frame number->request pair because it's not needed anymore.
1494            if (holder == null || sequenceCompleted) {
1495                iter.remove();
1496            }
1497
1498            // Call onCaptureSequenceCompleted
1499            if (sequenceCompleted) {
1500                Runnable resultDispatch = new Runnable() {
1501                    @Override
1502                    public void run() {
1503                        if (!CameraDeviceImpl.this.isClosed()){
1504                            if (DEBUG) {
1505                                Log.d(TAG, String.format(
1506                                        "fire sequence complete for request %d",
1507                                        requestId));
1508                            }
1509
1510                            holder.getCallback().onCaptureSequenceCompleted(
1511                                CameraDeviceImpl.this,
1512                                requestId,
1513                                requestLastFrameNumbers.getLastFrameNumber());
1514                        }
1515                    }
1516                };
1517                holder.getHandler().post(resultDispatch);
1518            }
1519        }
1520    }
1521
1522    public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
1523        //
1524        // Constants below need to be kept up-to-date with
1525        // frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h
1526        //
1527
1528        //
1529        // Error codes for onCameraError
1530        //
1531
1532        /**
1533         * Camera has been disconnected
1534         */
1535        public static final int ERROR_CAMERA_DISCONNECTED = 0;
1536        /**
1537         * Camera has encountered a device-level error
1538         * Matches CameraDevice.StateCallback#ERROR_CAMERA_DEVICE
1539         */
1540        public static final int ERROR_CAMERA_DEVICE = 1;
1541        /**
1542         * Camera has encountered a service-level error
1543         * Matches CameraDevice.StateCallback#ERROR_CAMERA_SERVICE
1544         */
1545        public static final int ERROR_CAMERA_SERVICE = 2;
1546        /**
1547         * Camera has encountered an error processing a single request.
1548         */
1549        public static final int ERROR_CAMERA_REQUEST = 3;
1550        /**
1551         * Camera has encountered an error producing metadata for a single capture
1552         */
1553        public static final int ERROR_CAMERA_RESULT = 4;
1554        /**
1555         * Camera has encountered an error producing an image buffer for a single capture
1556         */
1557        public static final int ERROR_CAMERA_BUFFER = 5;
1558
1559        @Override
1560        public IBinder asBinder() {
1561            return this;
1562        }
1563
1564        @Override
1565        public void onDeviceError(final int errorCode, CaptureResultExtras resultExtras) {
1566            if (DEBUG) {
1567                Log.d(TAG, String.format(
1568                        "Device error received, code %d, frame number %d, request ID %d, subseq ID %d",
1569                        errorCode, resultExtras.getFrameNumber(), resultExtras.getRequestId(),
1570                        resultExtras.getSubsequenceId()));
1571            }
1572
1573            synchronized(mInterfaceLock) {
1574                if (mRemoteDevice == null) {
1575                    return; // Camera already closed
1576                }
1577
1578                switch (errorCode) {
1579                    case ERROR_CAMERA_DISCONNECTED:
1580                        CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected);
1581                        break;
1582                    default:
1583                        Log.e(TAG, "Unknown error from camera device: " + errorCode);
1584                        // no break
1585                    case ERROR_CAMERA_DEVICE:
1586                    case ERROR_CAMERA_SERVICE:
1587                        mInError = true;
1588                        Runnable r = new Runnable() {
1589                            @Override
1590                            public void run() {
1591                                if (!CameraDeviceImpl.this.isClosed()) {
1592                                    mDeviceCallback.onError(CameraDeviceImpl.this, errorCode);
1593                                }
1594                            }
1595                        };
1596                        CameraDeviceImpl.this.mDeviceHandler.post(r);
1597                        break;
1598                    case ERROR_CAMERA_REQUEST:
1599                    case ERROR_CAMERA_RESULT:
1600                    case ERROR_CAMERA_BUFFER:
1601                        onCaptureErrorLocked(errorCode, resultExtras);
1602                        break;
1603                }
1604            }
1605        }
1606
1607        @Override
1608        public void onDeviceIdle() {
1609            if (DEBUG) {
1610                Log.d(TAG, "Camera now idle");
1611            }
1612            synchronized(mInterfaceLock) {
1613                if (mRemoteDevice == null) return; // Camera already closed
1614
1615                if (!CameraDeviceImpl.this.mIdle) {
1616                    CameraDeviceImpl.this.mDeviceHandler.post(mCallOnIdle);
1617                }
1618                CameraDeviceImpl.this.mIdle = true;
1619            }
1620        }
1621
1622        @Override
1623        public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) {
1624            int requestId = resultExtras.getRequestId();
1625            final long frameNumber = resultExtras.getFrameNumber();
1626
1627            if (DEBUG) {
1628                Log.d(TAG, "Capture started for id " + requestId + " frame number " + frameNumber);
1629            }
1630            final CaptureCallbackHolder holder;
1631
1632            synchronized(mInterfaceLock) {
1633                if (mRemoteDevice == null) return; // Camera already closed
1634
1635                // Get the callback for this frame ID, if there is one
1636                holder = CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId);
1637
1638                if (holder == null) {
1639                    return;
1640                }
1641
1642                if (isClosed()) return;
1643
1644                // Dispatch capture start notice
1645                holder.getHandler().post(
1646                    new Runnable() {
1647                        @Override
1648                        public void run() {
1649                            if (!CameraDeviceImpl.this.isClosed()) {
1650                                holder.getCallback().onCaptureStarted(
1651                                    CameraDeviceImpl.this,
1652                                    holder.getRequest(resultExtras.getSubsequenceId()),
1653                                    timestamp, frameNumber);
1654                            }
1655                        }
1656                    });
1657
1658            }
1659        }
1660
1661        @Override
1662        public void onResultReceived(CameraMetadataNative result,
1663                CaptureResultExtras resultExtras) throws RemoteException {
1664
1665            int requestId = resultExtras.getRequestId();
1666            long frameNumber = resultExtras.getFrameNumber();
1667
1668            if (DEBUG) {
1669                Log.v(TAG, "Received result frame " + frameNumber + " for id "
1670                        + requestId);
1671            }
1672
1673            synchronized(mInterfaceLock) {
1674                if (mRemoteDevice == null) return; // Camera already closed
1675
1676                // TODO: Handle CameraCharacteristics access from CaptureResult correctly.
1677                result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE,
1678                        getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE));
1679
1680                final CaptureCallbackHolder holder =
1681                        CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId);
1682                final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
1683
1684                boolean isPartialResult =
1685                        (resultExtras.getPartialResultCount() < mTotalPartialCount);
1686                boolean isReprocess = request.isReprocess();
1687
1688                // Check if we have a callback for this
1689                if (holder == null) {
1690                    if (DEBUG) {
1691                        Log.d(TAG,
1692                                "holder is null, early return at frame "
1693                                        + frameNumber);
1694                    }
1695
1696                    mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult,
1697                            isReprocess);
1698
1699                    return;
1700                }
1701
1702                if (isClosed()) {
1703                    if (DEBUG) {
1704                        Log.d(TAG,
1705                                "camera is closed, early return at frame "
1706                                        + frameNumber);
1707                    }
1708
1709                    mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult,
1710                            isReprocess);
1711                    return;
1712                }
1713
1714
1715                Runnable resultDispatch = null;
1716
1717                CaptureResult finalResult;
1718
1719                // Either send a partial result or the final capture completed result
1720                if (isPartialResult) {
1721                    final CaptureResult resultAsCapture =
1722                            new CaptureResult(result, request, resultExtras);
1723
1724                    // Partial result
1725                    resultDispatch = new Runnable() {
1726                        @Override
1727                        public void run() {
1728                            if (!CameraDeviceImpl.this.isClosed()){
1729                                holder.getCallback().onCaptureProgressed(
1730                                    CameraDeviceImpl.this,
1731                                    request,
1732                                    resultAsCapture);
1733                            }
1734                        }
1735                    };
1736
1737                    finalResult = resultAsCapture;
1738                } else {
1739                    List<CaptureResult> partialResults =
1740                            mFrameNumberTracker.popPartialResults(frameNumber);
1741
1742                    final TotalCaptureResult resultAsCapture = new TotalCaptureResult(result,
1743                            request, resultExtras, partialResults, holder.getSessionId());
1744
1745                    // Final capture result
1746                    resultDispatch = new Runnable() {
1747                        @Override
1748                        public void run() {
1749                            if (!CameraDeviceImpl.this.isClosed()){
1750                                holder.getCallback().onCaptureCompleted(
1751                                    CameraDeviceImpl.this,
1752                                    request,
1753                                    resultAsCapture);
1754                            }
1755                        }
1756                    };
1757
1758                    finalResult = resultAsCapture;
1759                }
1760
1761                holder.getHandler().post(resultDispatch);
1762
1763                // Collect the partials for a total result; or mark the frame as totally completed
1764                mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult,
1765                        isReprocess);
1766
1767                // Fire onCaptureSequenceCompleted
1768                if (!isPartialResult) {
1769                    checkAndFireSequenceComplete();
1770                }
1771            }
1772        }
1773
1774        @Override
1775        public void onPrepared(int streamId) {
1776            final OutputConfiguration output;
1777            final StateCallbackKK sessionCallback;
1778
1779            if (DEBUG) {
1780                Log.v(TAG, "Stream " + streamId + " is prepared");
1781            }
1782
1783            synchronized(mInterfaceLock) {
1784                output = mConfiguredOutputs.get(streamId);
1785                sessionCallback = mSessionStateCallback;
1786            }
1787
1788            if (sessionCallback == null) return;
1789
1790            if (output == null) {
1791                Log.w(TAG, "onPrepared invoked for unknown output Surface");
1792                return;
1793            }
1794            final Surface surface = output.getSurface();
1795
1796            sessionCallback.onSurfacePrepared(surface);
1797        }
1798
1799        /**
1800         * Called by onDeviceError for handling single-capture failures.
1801         */
1802        private void onCaptureErrorLocked(int errorCode, CaptureResultExtras resultExtras) {
1803
1804            final int requestId = resultExtras.getRequestId();
1805            final int subsequenceId = resultExtras.getSubsequenceId();
1806            final long frameNumber = resultExtras.getFrameNumber();
1807            final CaptureCallbackHolder holder =
1808                    CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId);
1809
1810            final CaptureRequest request = holder.getRequest(subsequenceId);
1811
1812            // No way to report buffer errors right now
1813            if (errorCode == ERROR_CAMERA_BUFFER) {
1814                Log.e(TAG, String.format("Lost output buffer reported for frame %d", frameNumber));
1815                return;
1816            }
1817
1818            boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT);
1819
1820            // This is only approximate - exact handling needs the camera service and HAL to
1821            // disambiguate between request failures to due abort and due to real errors.
1822            // For now, assume that if the session believes we're mid-abort, then the error
1823            // is due to abort.
1824            int reason = (mCurrentSession != null && mCurrentSession.isAborting()) ?
1825                    CaptureFailure.REASON_FLUSHED :
1826                    CaptureFailure.REASON_ERROR;
1827
1828            final CaptureFailure failure = new CaptureFailure(
1829                request,
1830                reason,
1831                /*dropped*/ mayHaveBuffers,
1832                requestId,
1833                frameNumber);
1834
1835            Runnable failureDispatch = new Runnable() {
1836                @Override
1837                public void run() {
1838                    if (!CameraDeviceImpl.this.isClosed()){
1839                        holder.getCallback().onCaptureFailed(
1840                            CameraDeviceImpl.this,
1841                            request,
1842                            failure);
1843                    }
1844                }
1845            };
1846            holder.getHandler().post(failureDispatch);
1847
1848            // Fire onCaptureSequenceCompleted if appropriate
1849            if (DEBUG) {
1850                Log.v(TAG, String.format("got error frame %d", frameNumber));
1851            }
1852            mFrameNumberTracker.updateTracker(frameNumber, /*error*/true, request.isReprocess());
1853            checkAndFireSequenceComplete();
1854        }
1855
1856    } // public class CameraDeviceCallbacks
1857
1858    /**
1859     * Default handler management.
1860     *
1861     * <p>
1862     * If handler is null, get the current thread's
1863     * Looper to create a Handler with. If no looper exists, throw {@code IllegalArgumentException}.
1864     * </p>
1865     */
1866    static Handler checkHandler(Handler handler) {
1867        if (handler == null) {
1868            Looper looper = Looper.myLooper();
1869            if (looper == null) {
1870                throw new IllegalArgumentException(
1871                    "No handler given, and current thread has no looper!");
1872            }
1873            handler = new Handler(looper);
1874        }
1875        return handler;
1876    }
1877
1878    /**
1879     * Default handler management, conditional on there being a callback.
1880     *
1881     * <p>If the callback isn't null, check the handler, otherwise pass it through.</p>
1882     */
1883    static <T> Handler checkHandler(Handler handler, T callback) {
1884        if (callback != null) {
1885            return checkHandler(handler);
1886        }
1887        return handler;
1888    }
1889
1890    private void checkIfCameraClosedOrInError() throws CameraAccessException {
1891        if (mRemoteDevice == null) {
1892            throw new IllegalStateException("CameraDevice was already closed");
1893        }
1894        if (mInError) {
1895            throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
1896                    "The camera device has encountered a serious error");
1897        }
1898    }
1899
1900    /** Whether the camera device has started to close (may not yet have finished) */
1901    private boolean isClosed() {
1902        return mClosing;
1903    }
1904
1905    private CameraCharacteristics getCharacteristics() {
1906        return mCharacteristics;
1907    }
1908
1909    @Override
1910    public void createConstrainedHighSpeedCaptureSession(List<Surface> outputs,
1911            android.hardware.camera2.CameraCaptureSession.StateCallback callback, Handler handler)
1912            throws CameraAccessException {
1913        // TODO: to be implemented
1914        throw new UnsupportedOperationException("To be implemented!!!!");
1915    }
1916
1917    @Override
1918    public List<CaptureRequest> createConstrainedHighSpeedRequestList(CaptureRequest request)
1919            throws CameraAccessException {
1920        throw new UnsupportedOperationException("To be implemented!!!!");
1921    }
1922}
1923