• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
16 package android.hardware.camera2.impl;
17 
18 import android.hardware.camera2.CameraAccessException;
19 import android.hardware.camera2.CameraCaptureSession;
20 import android.hardware.camera2.CameraDevice;
21 import android.hardware.camera2.CaptureRequest;
22 import android.hardware.camera2.ICameraDeviceUser;
23 import android.hardware.camera2.dispatch.ArgumentReplacingDispatcher;
24 import android.hardware.camera2.dispatch.BroadcastDispatcher;
25 import android.hardware.camera2.dispatch.DuckTypingDispatcher;
26 import android.hardware.camera2.dispatch.HandlerDispatcher;
27 import android.hardware.camera2.dispatch.InvokeDispatcher;
28 import android.hardware.camera2.params.OutputConfiguration;
29 import android.hardware.camera2.utils.TaskDrainer;
30 import android.hardware.camera2.utils.TaskSingleDrainer;
31 import android.os.Handler;
32 import android.util.Log;
33 import android.view.Surface;
34 
35 import java.util.Arrays;
36 import java.util.List;
37 
38 import static android.hardware.camera2.impl.CameraDeviceImpl.checkHandler;
39 import static com.android.internal.util.Preconditions.*;
40 
41 public 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      */
CameraCaptureSessionImpl(int id, Surface input, CameraCaptureSession.StateCallback callback, Handler stateHandler, android.hardware.camera2.impl.CameraDeviceImpl deviceImpl, Handler deviceStateHandler, boolean configureSuccess)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
getDevice()135     public CameraDevice getDevice() {
136         return mDeviceImpl;
137     }
138 
139     @Override
prepare(Surface surface)140     public void prepare(Surface surface) throws CameraAccessException {
141         mDeviceImpl.prepare(surface);
142     }
143 
144     @Override
prepare(int maxCount, Surface surface)145     public void prepare(int maxCount, Surface surface) throws CameraAccessException {
146         mDeviceImpl.prepare(maxCount, surface);
147     }
148 
149     @Override
tearDown(Surface surface)150     public void tearDown(Surface surface) throws CameraAccessException {
151         mDeviceImpl.tearDown(surface);
152     }
153 
154     @Override
finalizeOutputConfigurations( List<OutputConfiguration> outputConfigs)155     public void finalizeOutputConfigurations(
156             List<OutputConfiguration> outputConfigs) throws CameraAccessException {
157         mDeviceImpl.finalizeOutputConfigs(outputConfigs);
158     }
159 
160     @Override
capture(CaptureRequest request, CaptureCallback callback, Handler handler)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
captureBurst(List<CaptureRequest> requests, CaptureCallback callback, Handler handler)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
setRepeatingRequest(CaptureRequest request, CaptureCallback callback, Handler handler)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
setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback callback, Handler handler)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
stopRepeating()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
abortCaptures()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
isReprocessable()310     public boolean isReprocessable() {
311         return mInput != null;
312     }
313 
314     @Override
getInputSurface()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
replaceSessionClose()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
close()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
isAborting()426     public boolean isAborting() {
427         return mAborting;
428     }
429 
430     /**
431      * Post calls into a CameraCaptureSession.StateCallback to the user-specified {@code handler}.
432      */
createUserStateCallbackProxy(Handler handler, StateCallback callback)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")
createCaptureCallbackProxy( Handler handler, CaptureCallback callback)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
getDeviceStateCallback()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
finalize()655     protected void finalize() throws Throwable {
656         try {
657             close();
658         } finally {
659             super.finalize();
660         }
661     }
662 
checkNotClosed()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      */
addPendingSequence(int sequenceId)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      */
finishPendingSequence(int sequenceId)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
onDrained()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
onDrained()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
onDrained()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