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