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