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