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