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