Home | History | Annotate | Download | only in impl
      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      */
     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