CameraCaptureSessionImpl.java revision ec7f4c6ccddb3d8dfbd5b9ace2551350d030faa0
1/*
2 * Copyright (C) 2014 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 */
16package android.hardware.camera2.impl;
17
18import android.hardware.camera2.CameraAccessException;
19import android.hardware.camera2.CameraCaptureSession;
20import android.hardware.camera2.CameraDevice;
21import android.hardware.camera2.CaptureRequest;
22import android.hardware.camera2.ICameraDeviceUser;
23import android.hardware.camera2.dispatch.ArgumentReplacingDispatcher;
24import android.hardware.camera2.dispatch.BroadcastDispatcher;
25import android.hardware.camera2.dispatch.DuckTypingDispatcher;
26import android.hardware.camera2.dispatch.HandlerDispatcher;
27import android.hardware.camera2.dispatch.InvokeDispatcher;
28import android.hardware.camera2.params.OutputConfiguration;
29import android.hardware.camera2.utils.TaskDrainer;
30import android.hardware.camera2.utils.TaskSingleDrainer;
31import android.os.Handler;
32import android.util.Log;
33import android.view.Surface;
34
35import java.util.Arrays;
36import java.util.List;
37
38import static android.hardware.camera2.impl.CameraDeviceImpl.checkHandler;
39import static com.android.internal.util.Preconditions.*;
40
41public class CameraCaptureSessionImpl extends CameraCaptureSession
42        implements CameraCaptureSessionCore {
43    private static final String TAG = "CameraCaptureSession";
44    private static final boolean DEBUG = false;
45
46    /** Simple integer ID for session for debugging */
47    private final int mId;
48    private final String mIdString;
49
50    /** Input surface configured by native camera framework based on user-specified configuration */
51    private final Surface mInput;
52    /**
53     * User-specified state callback, used for outgoing events; calls to this object will be
54     * automatically {@link Handler#post(Runnable) posted} to {@code mStateHandler}.
55     */
56    private final CameraCaptureSession.StateCallback mStateCallback;
57    /** User-specified state handler used for outgoing state callback events */
58    private final Handler mStateHandler;
59
60    /** Internal camera device; used to translate calls into existing deprecated API */
61    private final android.hardware.camera2.impl.CameraDeviceImpl mDeviceImpl;
62    /** Internal handler; used for all incoming events to preserve total order */
63    private final Handler mDeviceHandler;
64
65    /** Drain Sequence IDs which have been queued but not yet finished with aborted/completed */
66    private final TaskDrainer<Integer> mSequenceDrainer;
67    /** Drain state transitions from ACTIVE -> IDLE */
68    private final TaskSingleDrainer mIdleDrainer;
69    /** Drain state transitions from BUSY -> IDLE */
70    private final TaskSingleDrainer mAbortDrainer;
71
72    /** This session is closed; all further calls will throw ISE */
73    private boolean mClosed = false;
74    /** This session failed to be configured successfully */
75    private final boolean mConfigureSuccess;
76    /** Do not unconfigure if this is set; another session will overwrite configuration */
77    private boolean mSkipUnconfigure = false;
78
79    /** Is the session in the process of aborting? Pay attention to BUSY->IDLE transitions. */
80    private volatile boolean mAborting;
81
82    /**
83     * Create a new CameraCaptureSession.
84     *
85     * <p>The camera device must already be in the {@code IDLE} state when this is invoked.
86     * There must be no pending actions
87     * (e.g. no pending captures, no repeating requests, no flush).</p>
88     */
89    CameraCaptureSessionImpl(int id, Surface input,
90            CameraCaptureSession.StateCallback callback, Handler stateHandler,
91            android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
92            Handler deviceStateHandler, boolean configureSuccess) {
93        if (callback == null) {
94            throw new IllegalArgumentException("callback must not be null");
95        }
96
97        mId = id;
98        mIdString = String.format("Session %d: ", mId);
99
100        mInput = input;
101        mStateHandler = checkHandler(stateHandler);
102        mStateCallback = createUserStateCallbackProxy(mStateHandler, callback);
103
104        mDeviceHandler = checkNotNull(deviceStateHandler, "deviceStateHandler must not be null");
105        mDeviceImpl = checkNotNull(deviceImpl, "deviceImpl must not be null");
106
107        /*
108         * Use the same handler as the device's StateCallback for all the internal coming events
109         *
110         * This ensures total ordering between CameraDevice.StateCallback and
111         * CameraDeviceImpl.CaptureCallback events.
112         */
113        mSequenceDrainer = new TaskDrainer<>(mDeviceHandler, new SequenceDrainListener(),
114                /*name*/"seq");
115        mIdleDrainer = new TaskSingleDrainer(mDeviceHandler, new IdleDrainListener(),
116                /*name*/"idle");
117        mAbortDrainer = new TaskSingleDrainer(mDeviceHandler, new AbortDrainListener(),
118                /*name*/"abort");
119
120        // CameraDevice should call configureOutputs and have it finish before constructing us
121
122        if (configureSuccess) {
123            mStateCallback.onConfigured(this);
124            if (DEBUG) Log.v(TAG, mIdString + "Created session successfully");
125            mConfigureSuccess = true;
126        } else {
127            mStateCallback.onConfigureFailed(this);
128            mClosed = true; // do not fire any other callbacks, do not allow any other work
129            Log.e(TAG, mIdString + "Failed to create capture session; configuration failed");
130            mConfigureSuccess = false;
131        }
132    }
133
134    @Override
135    public CameraDevice getDevice() {
136        return mDeviceImpl;
137    }
138
139    @Override
140    public void prepare(Surface surface) throws CameraAccessException {
141        mDeviceImpl.prepare(surface);
142    }
143
144    @Override
145    public void prepare(int maxCount, Surface surface) throws CameraAccessException {
146        mDeviceImpl.prepare(maxCount, surface);
147    }
148
149    @Override
150    public void tearDown(Surface surface) throws CameraAccessException {
151        mDeviceImpl.tearDown(surface);
152    }
153
154    @Override
155    public void finalizeOutputConfigurations(
156            List<OutputConfiguration> outputConfigs) throws CameraAccessException {
157        mDeviceImpl.finalizeOutputConfigs(outputConfigs);
158    }
159
160    @Override
161    public int capture(CaptureRequest request, CaptureCallback callback,
162            Handler handler) throws CameraAccessException {
163        if (request == null) {
164            throw new IllegalArgumentException("request must not be null");
165        } else if (request.isReprocess() && !isReprocessable()) {
166            throw new IllegalArgumentException("this capture session cannot handle reprocess " +
167                    "requests");
168        } else if (request.isReprocess() && request.getReprocessableSessionId() != mId) {
169            throw new IllegalArgumentException("capture request was created for another session");
170        }
171
172        synchronized (mDeviceImpl.mInterfaceLock) {
173            checkNotClosed();
174
175            handler = checkHandler(handler, callback);
176
177            if (DEBUG) {
178                Log.v(TAG, mIdString + "capture - request " + request + ", callback " + callback +
179                        " handler " + handler);
180            }
181
182            return addPendingSequence(mDeviceImpl.capture(request,
183                    createCaptureCallbackProxy(handler, callback), mDeviceHandler));
184        }
185    }
186
187    @Override
188    public int captureBurst(List<CaptureRequest> requests, CaptureCallback callback,
189            Handler handler) throws CameraAccessException {
190        if (requests == null) {
191            throw new IllegalArgumentException("Requests must not be null");
192        } else if (requests.isEmpty()) {
193            throw new IllegalArgumentException("Requests must have at least one element");
194        }
195
196        for (CaptureRequest request : requests) {
197            if (request.isReprocess()) {
198                if (!isReprocessable()) {
199                    throw new IllegalArgumentException("This capture session cannot handle " +
200                            "reprocess requests");
201                } else if (request.getReprocessableSessionId() != mId) {
202                    throw new IllegalArgumentException("Capture request was created for another " +
203                            "session");
204                }
205            }
206        }
207
208        synchronized (mDeviceImpl.mInterfaceLock) {
209            checkNotClosed();
210
211            handler = checkHandler(handler, callback);
212
213            if (DEBUG) {
214                CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
215                Log.v(TAG, mIdString + "captureBurst - requests " + Arrays.toString(requestArray) +
216                        ", callback " + callback + " handler " + handler);
217            }
218
219            return addPendingSequence(mDeviceImpl.captureBurst(requests,
220                    createCaptureCallbackProxy(handler, callback), mDeviceHandler));
221        }
222    }
223
224    @Override
225    public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback,
226            Handler handler) throws CameraAccessException {
227        if (request == null) {
228            throw new IllegalArgumentException("request must not be null");
229        } else if (request.isReprocess()) {
230            throw new IllegalArgumentException("repeating reprocess requests are not supported");
231        }
232
233        synchronized (mDeviceImpl.mInterfaceLock) {
234            checkNotClosed();
235
236            handler = checkHandler(handler, callback);
237
238            if (DEBUG) {
239                Log.v(TAG, mIdString + "setRepeatingRequest - request " + request + ", callback " +
240                        callback + " handler" + " " + handler);
241            }
242
243            return addPendingSequence(mDeviceImpl.setRepeatingRequest(request,
244                    createCaptureCallbackProxy(handler, callback), mDeviceHandler));
245        }
246    }
247
248    @Override
249    public int setRepeatingBurst(List<CaptureRequest> requests,
250            CaptureCallback callback, Handler handler) throws CameraAccessException {
251        if (requests == null) {
252            throw new IllegalArgumentException("requests must not be null");
253        } else if (requests.isEmpty()) {
254            throw new IllegalArgumentException("requests must have at least one element");
255        }
256
257        for (CaptureRequest r : requests) {
258            if (r.isReprocess()) {
259                throw new IllegalArgumentException("repeating reprocess burst requests are not " +
260                        "supported");
261            }
262        }
263
264        synchronized (mDeviceImpl.mInterfaceLock) {
265            checkNotClosed();
266
267            handler = checkHandler(handler, callback);
268
269            if (DEBUG) {
270                CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
271                Log.v(TAG, mIdString + "setRepeatingBurst - requests " +
272                        Arrays.toString(requestArray) + ", callback " + callback +
273                        " handler" + "" + handler);
274            }
275
276            return addPendingSequence(mDeviceImpl.setRepeatingBurst(requests,
277                    createCaptureCallbackProxy(handler, callback), mDeviceHandler));
278        }
279    }
280
281    @Override
282    public void stopRepeating() throws CameraAccessException {
283        synchronized (mDeviceImpl.mInterfaceLock) {
284            checkNotClosed();
285
286            if (DEBUG) {
287                Log.v(TAG, mIdString + "stopRepeating");
288            }
289
290            mDeviceImpl.stopRepeating();
291        }
292    }
293
294    @Override
295    public void abortCaptures() throws CameraAccessException {
296        synchronized (mDeviceImpl.mInterfaceLock) {
297            checkNotClosed();
298
299            if (DEBUG) {
300                Log.v(TAG, mIdString + "abortCaptures");
301            }
302
303            if (mAborting) {
304                Log.w(TAG, mIdString + "abortCaptures - Session is already aborting; doing nothing");
305                return;
306            }
307
308            mAborting = true;
309            mAbortDrainer.taskStarted();
310
311            mDeviceImpl.flush();
312            // The next BUSY -> IDLE set of transitions will mark the end of the abort.
313        }
314    }
315
316    @Override
317    public boolean isReprocessable() {
318        return mInput != null;
319    }
320
321    @Override
322    public Surface getInputSurface() {
323        return mInput;
324    }
325
326    /**
327     * Replace this session with another session.
328     *
329     * <p>This is an optimization to avoid unconfiguring and then immediately having to
330     * reconfigure again.</p>
331     *
332     * <p>The semantics are identical to {@link #close}, except that unconfiguring will be skipped.
333     * <p>
334     *
335     * <p>After this call completes, the session will not call any further methods on the camera
336     * device.</p>
337     *
338     * @see CameraCaptureSession#close
339     */
340    @Override
341    public void replaceSessionClose() {
342        synchronized (mDeviceImpl.mInterfaceLock) {
343            /*
344             * In order for creating new sessions to be fast, the new session should be created
345             * before the old session is closed.
346             *
347             * Otherwise the old session will always unconfigure if there is no new session to
348             * replace it.
349             *
350             * Unconfiguring could add hundreds of milliseconds of delay. We could race and attempt
351             * to skip unconfigure if a new session is created before the captures are all drained,
352             * but this would introduce nondeterministic behavior.
353             */
354
355            if (DEBUG) Log.v(TAG, mIdString + "replaceSessionClose");
356
357            // Set up fast shutdown. Possible alternative paths:
358            // - This session is active, so close() below starts the shutdown drain
359            // - This session is mid-shutdown drain, and hasn't yet reached the idle drain listener.
360            // - This session is already closed and has executed the idle drain listener, and
361            //   configureOutputsChecked(null) has already been called.
362            //
363            // Do not call configureOutputsChecked(null) going forward, since it would race with the
364            // configuration for the new session. If it was already called, then we don't care,
365            // since it won't get called again.
366            mSkipUnconfigure = true;
367            close();
368        }
369    }
370
371    @Override
372    public void close() {
373        synchronized (mDeviceImpl.mInterfaceLock) {
374            if (mClosed) {
375                if (DEBUG) Log.v(TAG, mIdString + "close - reentering");
376                return;
377            }
378
379            if (DEBUG) Log.v(TAG, mIdString + "close - first time");
380
381            mClosed = true;
382
383            /*
384             * Flush out any repeating request. Since camera is closed, no new requests
385             * can be queued, and eventually the entire request queue will be drained.
386             *
387             * If the camera device was already closed, short circuit and do nothing; since
388             * no more internal device callbacks will fire anyway.
389             *
390             * Otherwise, once stopRepeating is done, wait for camera to idle, then unconfigure
391             * the camera. Once that's done, fire #onClosed.
392             */
393            try {
394                mDeviceImpl.stopRepeating();
395            } catch (IllegalStateException e) {
396                // OK: Camera device may already be closed, nothing else to do
397
398                // TODO: Fire onClosed anytime we get the device onClosed or the ISE?
399                // or just suppress the ISE only and rely onClosed.
400                // Also skip any of the draining work if this is already closed.
401
402                // Short-circuit; queue callback immediately and return
403                mStateCallback.onClosed(this);
404                return;
405            } catch (CameraAccessException e) {
406                // OK: close does not throw checked exceptions.
407                Log.e(TAG, mIdString + "Exception while stopping repeating: ", e);
408
409                // TODO: call onError instead of onClosed if this happens
410            }
411
412            // If no sequences are pending, fire #onClosed immediately
413            mSequenceDrainer.beginDrain();
414        }
415    }
416
417    /**
418     * Whether currently in mid-abort.
419     *
420     * <p>This is used by the implementation to set the capture failure
421     * reason, in lieu of more accurate error codes from the camera service.
422     * Unsynchronized to avoid deadlocks between simultaneous session->device,
423     * device->session calls.</p>
424     *
425     */
426    @Override
427    public boolean isAborting() {
428        return mAborting;
429    }
430
431    /**
432     * Post calls into a CameraCaptureSession.StateCallback to the user-specified {@code handler}.
433     */
434    private StateCallback createUserStateCallbackProxy(Handler handler, StateCallback callback) {
435        InvokeDispatcher<StateCallback> userCallbackSink = new InvokeDispatcher<>(callback);
436        HandlerDispatcher<StateCallback> handlerPassthrough =
437                new HandlerDispatcher<>(userCallbackSink, handler);
438
439        return new CallbackProxies.SessionStateCallbackProxy(handlerPassthrough);
440    }
441
442    /**
443     * Forward callbacks from
444     * CameraDeviceImpl.CaptureCallback to the CameraCaptureSession.CaptureCallback.
445     *
446     * <p>In particular, all calls are automatically split to go both to our own
447     * internal callback, and to the user-specified callback (by transparently posting
448     * to the user-specified handler).</p>
449     *
450     * <p>When a capture sequence finishes, update the pending checked sequences set.</p>
451     */
452    @SuppressWarnings("deprecation")
453    private CameraDeviceImpl.CaptureCallback createCaptureCallbackProxy(
454            Handler handler, CaptureCallback callback) {
455        CameraDeviceImpl.CaptureCallback localCallback = new CameraDeviceImpl.CaptureCallback() {
456
457            @Override
458            public void onCaptureStarted(CameraDevice camera,
459                    CaptureRequest request, long timestamp, long frameNumber) {
460                // Do nothing
461            }
462
463            @Override
464            public void onCapturePartial(CameraDevice camera,
465                    CaptureRequest request, android.hardware.camera2.CaptureResult result) {
466                // Do nothing
467            }
468
469            @Override
470            public void onCaptureProgressed(CameraDevice camera,
471                    CaptureRequest request, android.hardware.camera2.CaptureResult partialResult) {
472                // Do nothing
473            }
474
475            @Override
476            public void onCaptureCompleted(CameraDevice camera,
477                    CaptureRequest request, android.hardware.camera2.TotalCaptureResult result) {
478                // Do nothing
479            }
480
481            @Override
482            public void onCaptureFailed(CameraDevice camera,
483                    CaptureRequest request, android.hardware.camera2.CaptureFailure failure) {
484                // Do nothing
485            }
486
487            @Override
488            public void onCaptureSequenceCompleted(CameraDevice camera,
489                    int sequenceId, long frameNumber) {
490                finishPendingSequence(sequenceId);
491            }
492
493            @Override
494            public void onCaptureSequenceAborted(CameraDevice camera,
495                    int sequenceId) {
496                finishPendingSequence(sequenceId);
497            }
498
499            @Override
500            public void onCaptureBufferLost(CameraDevice camera,
501                    CaptureRequest request, Surface target, long frameNumber) {
502                // Do nothing
503            }
504
505        };
506
507        /*
508         * Split the calls from the device callback into local callback and the following chain:
509         * - replace the first CameraDevice arg with a CameraCaptureSession
510         * - duck type from device callback to session callback
511         * - then forward the call to a handler
512         * - then finally invoke the destination method on the session callback object
513         */
514        if (callback == null) {
515            // OK: API allows the user to not specify a callback, and the handler may
516            // also be null in that case. Collapse whole dispatch chain to only call the local
517            // callback
518            return localCallback;
519        }
520
521        InvokeDispatcher<CameraDeviceImpl.CaptureCallback> localSink =
522                new InvokeDispatcher<>(localCallback);
523
524        InvokeDispatcher<CaptureCallback> userCallbackSink =
525                new InvokeDispatcher<>(callback);
526        HandlerDispatcher<CaptureCallback> handlerPassthrough =
527                new HandlerDispatcher<>(userCallbackSink, handler);
528        DuckTypingDispatcher<CameraDeviceImpl.CaptureCallback, CaptureCallback> duckToSession
529                = new DuckTypingDispatcher<>(handlerPassthrough, CaptureCallback.class);
530        ArgumentReplacingDispatcher<CameraDeviceImpl.CaptureCallback, CameraCaptureSessionImpl>
531                replaceDeviceWithSession = new ArgumentReplacingDispatcher<>(duckToSession,
532                        /*argumentIndex*/0, this);
533
534        BroadcastDispatcher<CameraDeviceImpl.CaptureCallback> broadcaster =
535                new BroadcastDispatcher<CameraDeviceImpl.CaptureCallback>(
536                    replaceDeviceWithSession,
537                    localSink);
538
539        return new CallbackProxies.DeviceCaptureCallbackProxy(broadcaster);
540    }
541
542    /**
543     *
544     * Create an internal state callback, to be invoked on the mDeviceHandler
545     *
546     * <p>It has a few behaviors:
547     * <ul>
548     * <li>Convert device state changes into session state changes.
549     * <li>Keep track of async tasks that the session began (idle, abort).
550     * </ul>
551     * </p>
552     * */
553    @Override
554    public CameraDeviceImpl.StateCallbackKK getDeviceStateCallback() {
555        final CameraCaptureSession session = this;
556        final Object interfaceLock = mDeviceImpl.mInterfaceLock;
557
558
559        return new CameraDeviceImpl.StateCallbackKK() {
560            private boolean mBusy = false;
561            private boolean mActive = false;
562
563            @Override
564            public void onOpened(CameraDevice camera) {
565                throw new AssertionError("Camera must already be open before creating a session");
566            }
567
568            @Override
569            public void onDisconnected(CameraDevice camera) {
570                if (DEBUG) Log.v(TAG, mIdString + "onDisconnected");
571                close();
572            }
573
574            @Override
575            public void onError(CameraDevice camera, int error) {
576                // Should not be reached, handled by device code
577                Log.wtf(TAG, mIdString + "Got device error " + error);
578            }
579
580            @Override
581            public void onActive(CameraDevice camera) {
582                mIdleDrainer.taskStarted();
583                mActive = true;
584
585                if (DEBUG) Log.v(TAG, mIdString + "onActive");
586                mStateCallback.onActive(session);
587            }
588
589            @Override
590            public void onIdle(CameraDevice camera) {
591                boolean isAborting;
592                if (DEBUG) Log.v(TAG, mIdString + "onIdle");
593
594                synchronized (interfaceLock) {
595                    isAborting = mAborting;
596                }
597
598                /*
599                 * Check which states we transitioned through:
600                 *
601                 * (ACTIVE -> IDLE)
602                 * (BUSY -> IDLE)
603                 *
604                 * Note that this is also legal:
605                 * (ACTIVE -> BUSY -> IDLE)
606                 *
607                 * and mark those tasks as finished
608                 */
609                if (mBusy && isAborting) {
610                    mAbortDrainer.taskFinished();
611
612                    synchronized (interfaceLock) {
613                        mAborting = false;
614                    }
615                }
616
617                if (mActive) {
618                    mIdleDrainer.taskFinished();
619                }
620
621                mBusy = false;
622                mActive = false;
623
624                mStateCallback.onReady(session);
625            }
626
627            @Override
628            public void onBusy(CameraDevice camera) {
629                mBusy = true;
630
631                // TODO: Queue captures during abort instead of failing them
632                // since the app won't be able to distinguish the two actives
633                // Don't signal the application since there's no clean mapping here
634                if (DEBUG) Log.v(TAG, mIdString + "onBusy");
635            }
636
637            @Override
638            public void onUnconfigured(CameraDevice camera) {
639                if (DEBUG) Log.v(TAG, mIdString + "onUnconfigured");
640            }
641
642            @Override
643            public void onRequestQueueEmpty() {
644                if (DEBUG) Log.v(TAG, mIdString + "onRequestQueueEmpty");
645                mStateCallback.onCaptureQueueEmpty(session);
646            }
647
648            @Override
649            public void onSurfacePrepared(Surface surface) {
650                if (DEBUG) Log.v(TAG, mIdString + "onSurfacePrepared");
651                mStateCallback.onSurfacePrepared(session, surface);
652            }
653        };
654
655    }
656
657    @Override
658    protected void finalize() throws Throwable {
659        try {
660            close();
661        } finally {
662            super.finalize();
663        }
664    }
665
666    private void checkNotClosed() {
667        if (mClosed) {
668            throw new IllegalStateException(
669                    "Session has been closed; further changes are illegal.");
670        }
671    }
672
673    /**
674     * Notify the session that a pending capture sequence has just been queued.
675     *
676     * <p>During a shutdown/close, the session waits until all pending sessions are finished
677     * before taking any further steps to shut down itself.</p>
678     *
679     * @see #finishPendingSequence
680     */
681    private int addPendingSequence(int sequenceId) {
682        mSequenceDrainer.taskStarted(sequenceId);
683        return sequenceId;
684    }
685
686    /**
687     * Notify the session that a pending capture sequence is now finished.
688     *
689     * <p>During a shutdown/close, once all pending sequences finish, it is safe to
690     * close the camera further by unconfiguring and then firing {@code onClosed}.</p>
691     */
692    private void finishPendingSequence(int sequenceId) {
693        try {
694            mSequenceDrainer.taskFinished(sequenceId);
695        } catch (IllegalStateException e) {
696            // Workaround for b/27870771
697            Log.w(TAG, e.getMessage());
698        }
699    }
700
701    private class SequenceDrainListener implements TaskDrainer.DrainListener {
702        @Override
703        public void onDrained() {
704            /*
705             * No repeating request is set; and the capture queue has fully drained.
706             *
707             * If no captures were queued to begin with, and an abort was queued,
708             * it's still possible to get another BUSY before the last IDLE.
709             *
710             * If the camera is already "IDLE" and no aborts are pending,
711             * then the drain immediately finishes.
712             */
713            if (DEBUG) Log.v(TAG, mIdString + "onSequenceDrained");
714
715
716            // Fire session close as soon as all sequences are complete.
717            // We may still need to unconfigure the device, but a new session might be created
718            // past this point, and notifications would then stop to this instance.
719            mStateCallback.onClosed(CameraCaptureSessionImpl.this);
720
721            // Fast path: A new capture session has replaced this one; don't wait for abort/idle
722            // as we won't get state updates any more anyway.
723            if (mSkipUnconfigure) {
724                return;
725            }
726
727            mAbortDrainer.beginDrain();
728        }
729    }
730
731    private class AbortDrainListener implements TaskDrainer.DrainListener {
732        @Override
733        public void onDrained() {
734            if (DEBUG) Log.v(TAG, mIdString + "onAbortDrained");
735            synchronized (mDeviceImpl.mInterfaceLock) {
736                /*
737                 * Any queued aborts have now completed.
738                 *
739                 * It's now safe to wait to receive the final "IDLE" event, as the camera device
740                 * will no longer again transition to "ACTIVE" by itself.
741                 *
742                 * If the camera is already "IDLE", then the drain immediately finishes.
743                 */
744
745                // Fast path: A new capture session has replaced this one; don't wait for idle
746                // as we won't get state updates any more anyway.
747                if (mSkipUnconfigure) {
748                    return;
749                }
750                mIdleDrainer.beginDrain();
751            }
752        }
753    }
754
755    private class IdleDrainListener implements TaskDrainer.DrainListener {
756        @Override
757        public void onDrained() {
758            if (DEBUG) Log.v(TAG, mIdString + "onIdleDrained");
759
760            // Take device lock before session lock so that we can call back into device
761            // without causing a deadlock
762            synchronized (mDeviceImpl.mInterfaceLock) {
763                /*
764                 * The device is now IDLE, and has settled. It will not transition to
765                 * ACTIVE or BUSY again by itself.
766                 *
767                 * It's now safe to unconfigure the outputs.
768                 *
769                 * This operation is idempotent; a session will not be closed twice.
770                 */
771                if (DEBUG)
772                    Log.v(TAG, mIdString + "Session drain complete, skip unconfigure: " +
773                            mSkipUnconfigure);
774
775                // Fast path: A new capture session has replaced this one; don't wait for idle
776                // as we won't get state updates any more anyway.
777                if (mSkipUnconfigure) {
778                    return;
779                }
780
781                // Final slow path: unconfigure the camera, no session has replaced us and
782                // everything is idle.
783                try {
784                    // begin transition to unconfigured
785                    mDeviceImpl.configureStreamsChecked(/*inputConfig*/null, /*outputs*/null,
786                            /*operatingMode*/ ICameraDeviceUser.NORMAL_MODE);
787                } catch (CameraAccessException e) {
788                    // OK: do not throw checked exceptions.
789                    Log.e(TAG, mIdString + "Exception while unconfiguring outputs: ", e);
790
791                    // TODO: call onError instead of onClosed if this happens
792                } catch (IllegalStateException e) {
793                    // Camera is already closed, so nothing left to do
794                    if (DEBUG) Log.v(TAG, mIdString +
795                            "Camera was already closed or busy, skipping unconfigure");
796                }
797            }
798        }
799    }
800
801}
802