Home | History | Annotate | Download | only in impl
      1 /*
      2  * Copyright (C) 2013 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 
     17 package android.hardware.camera2.impl;
     18 
     19 import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
     20 
     21 import android.graphics.ImageFormat;
     22 import android.hardware.camera2.CameraAccessException;
     23 import android.hardware.camera2.CameraCaptureSession;
     24 import android.hardware.camera2.CameraCharacteristics;
     25 import android.hardware.camera2.CameraDevice;
     26 import android.hardware.camera2.CaptureRequest;
     27 import android.hardware.camera2.CaptureResult;
     28 import android.hardware.camera2.CaptureFailure;
     29 import android.hardware.camera2.ICameraDeviceCallbacks;
     30 import android.hardware.camera2.ICameraDeviceUser;
     31 import android.hardware.camera2.TotalCaptureResult;
     32 import android.hardware.camera2.params.InputConfiguration;
     33 import android.hardware.camera2.params.OutputConfiguration;
     34 import android.hardware.camera2.params.ReprocessFormatsMap;
     35 import android.hardware.camera2.params.StreamConfigurationMap;
     36 import android.hardware.camera2.utils.SubmitInfo;
     37 import android.hardware.camera2.utils.SurfaceUtils;
     38 import android.hardware.ICameraService;
     39 import android.os.Handler;
     40 import android.os.IBinder;
     41 import android.os.Looper;
     42 import android.os.RemoteException;
     43 import android.os.ServiceSpecificException;
     44 import android.util.Log;
     45 import android.util.Range;
     46 import android.util.Size;
     47 import android.util.SparseArray;
     48 import android.view.Surface;
     49 
     50 import java.util.AbstractMap.SimpleEntry;
     51 import java.util.ArrayList;
     52 import java.util.Arrays;
     53 import java.util.Collection;
     54 import java.util.Collections;
     55 import java.util.concurrent.atomic.AtomicBoolean;
     56 import java.util.HashMap;
     57 import java.util.HashSet;
     58 import java.util.Iterator;
     59 import java.util.List;
     60 import java.util.LinkedList;
     61 import java.util.TreeMap;
     62 
     63 /**
     64  * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
     65  */
     66 public class CameraDeviceImpl extends CameraDevice
     67         implements IBinder.DeathRecipient {
     68     private final String TAG;
     69     private final boolean DEBUG = false;
     70 
     71     private static final int REQUEST_ID_NONE = -1;
     72 
     73     // TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
     74     private ICameraDeviceUserWrapper mRemoteDevice;
     75 
     76     // Lock to synchronize cross-thread access to device public interface
     77     final Object mInterfaceLock = new Object(); // access from this class and Session only!
     78     private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
     79 
     80     private final StateCallback mDeviceCallback;
     81     private volatile StateCallbackKK mSessionStateCallback;
     82     private final Handler mDeviceHandler;
     83 
     84     private final AtomicBoolean mClosing = new AtomicBoolean();
     85     private boolean mInError = false;
     86     private boolean mIdle = true;
     87 
     88     /** map request IDs to callback/request data */
     89     private final SparseArray<CaptureCallbackHolder> mCaptureCallbackMap =
     90             new SparseArray<CaptureCallbackHolder>();
     91 
     92     private int mRepeatingRequestId = REQUEST_ID_NONE;
     93     // Map stream IDs to input/output configurations
     94     private SimpleEntry<Integer, InputConfiguration> mConfiguredInput =
     95             new SimpleEntry<>(REQUEST_ID_NONE, null);
     96     private final SparseArray<OutputConfiguration> mConfiguredOutputs =
     97             new SparseArray<>();
     98 
     99     private final String mCameraId;
    100     private final CameraCharacteristics mCharacteristics;
    101     private final int mTotalPartialCount;
    102 
    103     /**
    104      * A list tracking request and its expected last regular frame number and last reprocess frame
    105      * number. Updated when calling ICameraDeviceUser methods.
    106      */
    107     private final List<RequestLastFrameNumbersHolder> mRequestLastFrameNumbersList =
    108             new ArrayList<>();
    109 
    110     /**
    111      * An object tracking received frame numbers.
    112      * Updated when receiving callbacks from ICameraDeviceCallbacks.
    113      */
    114     private final FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker();
    115 
    116     private CameraCaptureSessionCore mCurrentSession;
    117     private int mNextSessionId = 0;
    118 
    119     // Runnables for all state transitions, except error, which needs the
    120     // error code argument
    121 
    122     private final Runnable mCallOnOpened = new Runnable() {
    123         @Override
    124         public void run() {
    125             StateCallbackKK sessionCallback = null;
    126             synchronized(mInterfaceLock) {
    127                 if (mRemoteDevice == null) return; // Camera already closed
    128 
    129                 sessionCallback = mSessionStateCallback;
    130             }
    131             if (sessionCallback != null) {
    132                 sessionCallback.onOpened(CameraDeviceImpl.this);
    133             }
    134             mDeviceCallback.onOpened(CameraDeviceImpl.this);
    135         }
    136     };
    137 
    138     private final Runnable mCallOnUnconfigured = new Runnable() {
    139         @Override
    140         public void run() {
    141             StateCallbackKK sessionCallback = null;
    142             synchronized(mInterfaceLock) {
    143                 if (mRemoteDevice == null) return; // Camera already closed
    144 
    145                 sessionCallback = mSessionStateCallback;
    146             }
    147             if (sessionCallback != null) {
    148                 sessionCallback.onUnconfigured(CameraDeviceImpl.this);
    149             }
    150         }
    151     };
    152 
    153     private final Runnable mCallOnActive = new Runnable() {
    154         @Override
    155         public void run() {
    156             StateCallbackKK sessionCallback = null;
    157             synchronized(mInterfaceLock) {
    158                 if (mRemoteDevice == null) return; // Camera already closed
    159 
    160                 sessionCallback = mSessionStateCallback;
    161             }
    162             if (sessionCallback != null) {
    163                 sessionCallback.onActive(CameraDeviceImpl.this);
    164             }
    165         }
    166     };
    167 
    168     private final Runnable mCallOnBusy = new Runnable() {
    169         @Override
    170         public void run() {
    171             StateCallbackKK sessionCallback = null;
    172             synchronized(mInterfaceLock) {
    173                 if (mRemoteDevice == null) return; // Camera already closed
    174 
    175                 sessionCallback = mSessionStateCallback;
    176             }
    177             if (sessionCallback != null) {
    178                 sessionCallback.onBusy(CameraDeviceImpl.this);
    179             }
    180         }
    181     };
    182 
    183     private final Runnable mCallOnClosed = new Runnable() {
    184         private boolean mClosedOnce = false;
    185 
    186         @Override
    187         public void run() {
    188             if (mClosedOnce) {
    189                 throw new AssertionError("Don't post #onClosed more than once");
    190             }
    191             StateCallbackKK sessionCallback = null;
    192             synchronized(mInterfaceLock) {
    193                 sessionCallback = mSessionStateCallback;
    194             }
    195             if (sessionCallback != null) {
    196                 sessionCallback.onClosed(CameraDeviceImpl.this);
    197             }
    198             mDeviceCallback.onClosed(CameraDeviceImpl.this);
    199             mClosedOnce = true;
    200         }
    201     };
    202 
    203     private final Runnable mCallOnIdle = new Runnable() {
    204         @Override
    205         public void run() {
    206             StateCallbackKK sessionCallback = null;
    207             synchronized(mInterfaceLock) {
    208                 if (mRemoteDevice == null) return; // Camera already closed
    209 
    210                 sessionCallback = mSessionStateCallback;
    211             }
    212             if (sessionCallback != null) {
    213                 sessionCallback.onIdle(CameraDeviceImpl.this);
    214             }
    215         }
    216     };
    217 
    218     private final Runnable mCallOnDisconnected = new Runnable() {
    219         @Override
    220         public void run() {
    221             StateCallbackKK sessionCallback = null;
    222             synchronized(mInterfaceLock) {
    223                 if (mRemoteDevice == null) return; // Camera already closed
    224 
    225                 sessionCallback = mSessionStateCallback;
    226             }
    227             if (sessionCallback != null) {
    228                 sessionCallback.onDisconnected(CameraDeviceImpl.this);
    229             }
    230             mDeviceCallback.onDisconnected(CameraDeviceImpl.this);
    231         }
    232     };
    233 
    234     public CameraDeviceImpl(String cameraId, StateCallback callback, Handler handler,
    235                         CameraCharacteristics characteristics) {
    236         if (cameraId == null || callback == null || handler == null || characteristics == null) {
    237             throw new IllegalArgumentException("Null argument given");
    238         }
    239         mCameraId = cameraId;
    240         mDeviceCallback = callback;
    241         mDeviceHandler = handler;
    242         mCharacteristics = characteristics;
    243 
    244         final int MAX_TAG_LEN = 23;
    245         String tag = String.format("CameraDevice-JV-%s", mCameraId);
    246         if (tag.length() > MAX_TAG_LEN) {
    247             tag = tag.substring(0, MAX_TAG_LEN);
    248         }
    249         TAG = tag;
    250 
    251         Integer partialCount =
    252                 mCharacteristics.get(CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT);
    253         if (partialCount == null) {
    254             // 1 means partial result is not supported.
    255             mTotalPartialCount = 1;
    256         } else {
    257             mTotalPartialCount = partialCount;
    258         }
    259     }
    260 
    261     public CameraDeviceCallbacks getCallbacks() {
    262         return mCallbacks;
    263     }
    264 
    265     /**
    266      * Set remote device, which triggers initial onOpened/onUnconfigured callbacks
    267      *
    268      * <p>This function may post onDisconnected and throw CAMERA_DISCONNECTED if remoteDevice dies
    269      * during setup.</p>
    270      *
    271      */
    272     public void setRemoteDevice(ICameraDeviceUser remoteDevice) throws CameraAccessException {
    273         synchronized(mInterfaceLock) {
    274             // TODO: Move from decorator to direct binder-mediated exceptions
    275             // If setRemoteFailure already called, do nothing
    276             if (mInError) return;
    277 
    278             mRemoteDevice = new ICameraDeviceUserWrapper(remoteDevice);
    279 
    280             IBinder remoteDeviceBinder = remoteDevice.asBinder();
    281             // For legacy camera device, remoteDevice is in the same process, and
    282             // asBinder returns NULL.
    283             if (remoteDeviceBinder != null) {
    284                 try {
    285                     remoteDeviceBinder.linkToDeath(this, /*flag*/ 0);
    286                 } catch (RemoteException e) {
    287                     CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected);
    288 
    289                     throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
    290                             "The camera device has encountered a serious error");
    291                 }
    292             }
    293 
    294             mDeviceHandler.post(mCallOnOpened);
    295             mDeviceHandler.post(mCallOnUnconfigured);
    296         }
    297     }
    298 
    299     /**
    300      * Call to indicate failed connection to a remote camera device.
    301      *
    302      * <p>This places the camera device in the error state and informs the callback.
    303      * Use in place of setRemoteDevice() when startup fails.</p>
    304      */
    305     public void setRemoteFailure(final ServiceSpecificException failure) {
    306         int failureCode = StateCallback.ERROR_CAMERA_DEVICE;
    307         boolean failureIsError = true;
    308 
    309         switch (failure.errorCode) {
    310             case ICameraService.ERROR_CAMERA_IN_USE:
    311                 failureCode = StateCallback.ERROR_CAMERA_IN_USE;
    312                 break;
    313             case ICameraService.ERROR_MAX_CAMERAS_IN_USE:
    314                 failureCode = StateCallback.ERROR_MAX_CAMERAS_IN_USE;
    315                 break;
    316             case ICameraService.ERROR_DISABLED:
    317                 failureCode = StateCallback.ERROR_CAMERA_DISABLED;
    318                 break;
    319             case ICameraService.ERROR_DISCONNECTED:
    320                 failureIsError = false;
    321                 break;
    322             case ICameraService.ERROR_INVALID_OPERATION:
    323                 failureCode = StateCallback.ERROR_CAMERA_DEVICE;
    324                 break;
    325             default:
    326                 Log.e(TAG, "Unexpected failure in opening camera device: " + failure.errorCode +
    327                         failure.getMessage());
    328                 break;
    329         }
    330         final int code = failureCode;
    331         final boolean isError = failureIsError;
    332         synchronized(mInterfaceLock) {
    333             mInError = true;
    334             mDeviceHandler.post(new Runnable() {
    335                 @Override
    336                 public void run() {
    337                     if (isError) {
    338                         mDeviceCallback.onError(CameraDeviceImpl.this, code);
    339                     } else {
    340                         mDeviceCallback.onDisconnected(CameraDeviceImpl.this);
    341                     }
    342                 }
    343             });
    344         }
    345     }
    346 
    347     @Override
    348     public String getId() {
    349         return mCameraId;
    350     }
    351 
    352     public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
    353         // Leave this here for backwards compatibility with older code using this directly
    354         ArrayList<OutputConfiguration> outputConfigs = new ArrayList<>(outputs.size());
    355         for (Surface s : outputs) {
    356             outputConfigs.add(new OutputConfiguration(s));
    357         }
    358         configureStreamsChecked(/*inputConfig*/null, outputConfigs,
    359                 /*isConstrainedHighSpeed*/false);
    360 
    361     }
    362 
    363     /**
    364      * Attempt to configure the input and outputs; the device goes to idle and then configures the
    365      * new input and outputs if possible.
    366      *
    367      * <p>The configuration may gracefully fail, if input configuration is not supported,
    368      * if there are too many outputs, if the formats are not supported, or if the sizes for that
    369      * format is not supported. In this case this function will return {@code false} and the
    370      * unconfigured callback will be fired.</p>
    371      *
    372      * <p>If the configuration succeeds (with 1 or more outputs with or without an input),
    373      * then the idle callback is fired. Unconfiguring the device always fires the idle callback.</p>
    374      *
    375      * @param inputConfig input configuration or {@code null} for no input
    376      * @param outputs a list of one or more surfaces, or {@code null} to unconfigure
    377      * @param isConstrainedHighSpeed If the streams configuration is for constrained high speed output.
    378      * @return whether or not the configuration was successful
    379      *
    380      * @throws CameraAccessException if there were any unexpected problems during configuration
    381      */
    382     public boolean configureStreamsChecked(InputConfiguration inputConfig,
    383             List<OutputConfiguration> outputs, boolean isConstrainedHighSpeed)
    384                     throws CameraAccessException {
    385         // Treat a null input the same an empty list
    386         if (outputs == null) {
    387             outputs = new ArrayList<OutputConfiguration>();
    388         }
    389         if (outputs.size() == 0 && inputConfig != null) {
    390             throw new IllegalArgumentException("cannot configure an input stream without " +
    391                     "any output streams");
    392         }
    393 
    394         checkInputConfiguration(inputConfig);
    395 
    396         boolean success = false;
    397 
    398         synchronized(mInterfaceLock) {
    399             checkIfCameraClosedOrInError();
    400             // Streams to create
    401             HashSet<OutputConfiguration> addSet = new HashSet<OutputConfiguration>(outputs);
    402             // Streams to delete
    403             List<Integer> deleteList = new ArrayList<Integer>();
    404 
    405             // Determine which streams need to be created, which to be deleted
    406             for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
    407                 int streamId = mConfiguredOutputs.keyAt(i);
    408                 OutputConfiguration outConfig = mConfiguredOutputs.valueAt(i);
    409 
    410                 if (!outputs.contains(outConfig) || outConfig.isDeferredConfiguration()) {
    411                     // Always delete the deferred output configuration when the session
    412                     // is created, as the deferred output configuration doesn't have unique surface
    413                     // related identifies.
    414                     deleteList.add(streamId);
    415                 } else {
    416                     addSet.remove(outConfig);  // Don't create a stream previously created
    417                 }
    418             }
    419 
    420             mDeviceHandler.post(mCallOnBusy);
    421             stopRepeating();
    422 
    423             try {
    424                 waitUntilIdle();
    425 
    426                 mRemoteDevice.beginConfigure();
    427 
    428                 // reconfigure the input stream if the input configuration is different.
    429                 InputConfiguration currentInputConfig = mConfiguredInput.getValue();
    430                 if (inputConfig != currentInputConfig &&
    431                         (inputConfig == null || !inputConfig.equals(currentInputConfig))) {
    432                     if (currentInputConfig != null) {
    433                         mRemoteDevice.deleteStream(mConfiguredInput.getKey());
    434                         mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>(
    435                                 REQUEST_ID_NONE, null);
    436                     }
    437                     if (inputConfig != null) {
    438                         int streamId = mRemoteDevice.createInputStream(inputConfig.getWidth(),
    439                                 inputConfig.getHeight(), inputConfig.getFormat());
    440                         mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>(
    441                                 streamId, inputConfig);
    442                     }
    443                 }
    444 
    445                 // Delete all streams first (to free up HW resources)
    446                 for (Integer streamId : deleteList) {
    447                     mRemoteDevice.deleteStream(streamId);
    448                     mConfiguredOutputs.delete(streamId);
    449                 }
    450 
    451                 // Add all new streams
    452                 for (OutputConfiguration outConfig : outputs) {
    453                     if (addSet.contains(outConfig)) {
    454                         int streamId = mRemoteDevice.createStream(outConfig);
    455                         mConfiguredOutputs.put(streamId, outConfig);
    456                     }
    457                 }
    458 
    459                 mRemoteDevice.endConfigure(isConstrainedHighSpeed);
    460 
    461                 success = true;
    462             } catch (IllegalArgumentException e) {
    463                 // OK. camera service can reject stream config if it's not supported by HAL
    464                 // This is only the result of a programmer misusing the camera2 api.
    465                 Log.w(TAG, "Stream configuration failed due to: " + e.getMessage());
    466                 return false;
    467             } catch (CameraAccessException e) {
    468                 if (e.getReason() == CameraAccessException.CAMERA_IN_USE) {
    469                     throw new IllegalStateException("The camera is currently busy." +
    470                             " You must wait until the previous operation completes.", e);
    471                 }
    472                 throw e;
    473             } finally {
    474                 if (success && outputs.size() > 0) {
    475                     mDeviceHandler.post(mCallOnIdle);
    476                 } else {
    477                     // Always return to the 'unconfigured' state if we didn't hit a fatal error
    478                     mDeviceHandler.post(mCallOnUnconfigured);
    479                 }
    480             }
    481         }
    482 
    483         return success;
    484     }
    485 
    486     @Override
    487     public void createCaptureSession(List<Surface> outputs,
    488             CameraCaptureSession.StateCallback callback, Handler handler)
    489             throws CameraAccessException {
    490         List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size());
    491         for (Surface surface : outputs) {
    492             outConfigurations.add(new OutputConfiguration(surface));
    493         }
    494         createCaptureSessionInternal(null, outConfigurations, callback, handler,
    495                 /*isConstrainedHighSpeed*/false);
    496     }
    497 
    498     @Override
    499     public void createCaptureSessionByOutputConfigurations(
    500             List<OutputConfiguration> outputConfigurations,
    501             CameraCaptureSession.StateCallback callback, Handler handler)
    502             throws CameraAccessException {
    503         if (DEBUG) {
    504             Log.d(TAG, "createCaptureSessionByOutputConfiguration");
    505         }
    506 
    507         // OutputConfiguration objects are immutable, but need to have our own array
    508         List<OutputConfiguration> currentOutputs = new ArrayList<>(outputConfigurations);
    509 
    510         createCaptureSessionInternal(null, currentOutputs, callback, handler,
    511                 /*isConstrainedHighSpeed*/false);
    512     }
    513 
    514     @Override
    515     public void createReprocessableCaptureSession(InputConfiguration inputConfig,
    516             List<Surface> outputs, CameraCaptureSession.StateCallback callback, Handler handler)
    517             throws CameraAccessException {
    518         if (DEBUG) {
    519             Log.d(TAG, "createReprocessableCaptureSession");
    520         }
    521 
    522         if (inputConfig == null) {
    523             throw new IllegalArgumentException("inputConfig cannot be null when creating a " +
    524                     "reprocessable capture session");
    525         }
    526         List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size());
    527         for (Surface surface : outputs) {
    528             outConfigurations.add(new OutputConfiguration(surface));
    529         }
    530         createCaptureSessionInternal(inputConfig, outConfigurations, callback, handler,
    531                 /*isConstrainedHighSpeed*/false);
    532     }
    533 
    534     @Override
    535     public void createReprocessableCaptureSessionByConfigurations(InputConfiguration inputConfig,
    536             List<OutputConfiguration> outputs,
    537             android.hardware.camera2.CameraCaptureSession.StateCallback callback, Handler handler)
    538                     throws CameraAccessException {
    539         if (DEBUG) {
    540             Log.d(TAG, "createReprocessableCaptureSessionWithConfigurations");
    541         }
    542 
    543         if (inputConfig == null) {
    544             throw new IllegalArgumentException("inputConfig cannot be null when creating a " +
    545                     "reprocessable capture session");
    546         }
    547 
    548         if (outputs == null) {
    549             throw new IllegalArgumentException("Output configurations cannot be null when " +
    550                     "creating a reprocessable capture session");
    551         }
    552 
    553         // OutputConfiguration objects aren't immutable, make a copy before using.
    554         List<OutputConfiguration> currentOutputs = new ArrayList<OutputConfiguration>();
    555         for (OutputConfiguration output : outputs) {
    556             currentOutputs.add(new OutputConfiguration(output));
    557         }
    558         createCaptureSessionInternal(inputConfig, currentOutputs,
    559                 callback, handler, /*isConstrainedHighSpeed*/false);
    560     }
    561 
    562     @Override
    563     public void createConstrainedHighSpeedCaptureSession(List<Surface> outputs,
    564             android.hardware.camera2.CameraCaptureSession.StateCallback callback, Handler handler)
    565             throws CameraAccessException {
    566         if (outputs == null || outputs.size() == 0 || outputs.size() > 2) {
    567             throw new IllegalArgumentException(
    568                     "Output surface list must not be null and the size must be no more than 2");
    569         }
    570         StreamConfigurationMap config =
    571                 getCharacteristics().get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    572         SurfaceUtils.checkConstrainedHighSpeedSurfaces(outputs, /*fpsRange*/null, config);
    573 
    574         List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size());
    575         for (Surface surface : outputs) {
    576             outConfigurations.add(new OutputConfiguration(surface));
    577         }
    578         createCaptureSessionInternal(null, outConfigurations, callback, handler,
    579                 /*isConstrainedHighSpeed*/true);
    580     }
    581 
    582     private void createCaptureSessionInternal(InputConfiguration inputConfig,
    583             List<OutputConfiguration> outputConfigurations,
    584             CameraCaptureSession.StateCallback callback, Handler handler,
    585             boolean isConstrainedHighSpeed) throws CameraAccessException {
    586         synchronized(mInterfaceLock) {
    587             if (DEBUG) {
    588                 Log.d(TAG, "createCaptureSessionInternal");
    589             }
    590 
    591             checkIfCameraClosedOrInError();
    592 
    593             if (isConstrainedHighSpeed && inputConfig != null) {
    594                 throw new IllegalArgumentException("Constrained high speed session doesn't support"
    595                         + " input configuration yet.");
    596             }
    597 
    598             // Notify current session that it's going away, before starting camera operations
    599             // After this call completes, the session is not allowed to call into CameraDeviceImpl
    600             if (mCurrentSession != null) {
    601                 mCurrentSession.replaceSessionClose();
    602             }
    603 
    604             // TODO: dont block for this
    605             boolean configureSuccess = true;
    606             CameraAccessException pendingException = null;
    607             Surface input = null;
    608             try {
    609                 // configure streams and then block until IDLE
    610                 configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations,
    611                         isConstrainedHighSpeed);
    612                 if (configureSuccess == true && inputConfig != null) {
    613                     input = mRemoteDevice.getInputSurface();
    614                 }
    615             } catch (CameraAccessException e) {
    616                 configureSuccess = false;
    617                 pendingException = e;
    618                 input = null;
    619                 if (DEBUG) {
    620                     Log.v(TAG, "createCaptureSession - failed with exception ", e);
    621                 }
    622             }
    623 
    624             List<Surface> outSurfaces = new ArrayList<>(outputConfigurations.size());
    625             for (OutputConfiguration config : outputConfigurations) {
    626                 outSurfaces.add(config.getSurface());
    627             }
    628             // Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise.
    629             CameraCaptureSessionCore newSession = null;
    630             if (isConstrainedHighSpeed) {
    631                 newSession = new CameraConstrainedHighSpeedCaptureSessionImpl(mNextSessionId++,
    632                         outSurfaces, callback, handler, this, mDeviceHandler, configureSuccess,
    633                         mCharacteristics);
    634             } else {
    635                 newSession = new CameraCaptureSessionImpl(mNextSessionId++, input,
    636                         outSurfaces, callback, handler, this, mDeviceHandler,
    637                         configureSuccess);
    638             }
    639 
    640             // TODO: wait until current session closes, then create the new session
    641             mCurrentSession = newSession;
    642 
    643             if (pendingException != null) {
    644                 throw pendingException;
    645             }
    646 
    647             mSessionStateCallback = mCurrentSession.getDeviceStateCallback();
    648         }
    649     }
    650 
    651     /**
    652      * For use by backwards-compatibility code only.
    653      */
    654     public void setSessionListener(StateCallbackKK sessionCallback) {
    655         synchronized(mInterfaceLock) {
    656             mSessionStateCallback = sessionCallback;
    657         }
    658     }
    659 
    660     @Override
    661     public CaptureRequest.Builder createCaptureRequest(int templateType)
    662             throws CameraAccessException {
    663         synchronized(mInterfaceLock) {
    664             checkIfCameraClosedOrInError();
    665 
    666             CameraMetadataNative templatedRequest = null;
    667 
    668             templatedRequest = mRemoteDevice.createDefaultRequest(templateType);
    669 
    670             CaptureRequest.Builder builder = new CaptureRequest.Builder(
    671                     templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
    672 
    673             return builder;
    674         }
    675     }
    676 
    677     @Override
    678     public CaptureRequest.Builder createReprocessCaptureRequest(TotalCaptureResult inputResult)
    679             throws CameraAccessException {
    680         synchronized(mInterfaceLock) {
    681             checkIfCameraClosedOrInError();
    682 
    683             CameraMetadataNative resultMetadata = new
    684                     CameraMetadataNative(inputResult.getNativeCopy());
    685 
    686             return new CaptureRequest.Builder(resultMetadata, /*reprocess*/true,
    687                     inputResult.getSessionId());
    688         }
    689     }
    690 
    691     public void prepare(Surface surface) throws CameraAccessException {
    692         if (surface == null) throw new IllegalArgumentException("Surface is null");
    693 
    694         synchronized(mInterfaceLock) {
    695             int streamId = -1;
    696             for (int i = 0; i < mConfiguredOutputs.size(); i++) {
    697                 if (surface == mConfiguredOutputs.valueAt(i).getSurface()) {
    698                     streamId = mConfiguredOutputs.keyAt(i);
    699                     break;
    700                 }
    701             }
    702             if (streamId == -1) {
    703                 throw new IllegalArgumentException("Surface is not part of this session");
    704             }
    705 
    706             mRemoteDevice.prepare(streamId);
    707         }
    708     }
    709 
    710     public void prepare(int maxCount, Surface surface) throws CameraAccessException {
    711         if (surface == null) throw new IllegalArgumentException("Surface is null");
    712         if (maxCount <= 0) throw new IllegalArgumentException("Invalid maxCount given: " +
    713                 maxCount);
    714 
    715         synchronized(mInterfaceLock) {
    716             int streamId = -1;
    717             for (int i = 0; i < mConfiguredOutputs.size(); i++) {
    718                 if (surface == mConfiguredOutputs.valueAt(i).getSurface()) {
    719                     streamId = mConfiguredOutputs.keyAt(i);
    720                     break;
    721                 }
    722             }
    723             if (streamId == -1) {
    724                 throw new IllegalArgumentException("Surface is not part of this session");
    725             }
    726 
    727             mRemoteDevice.prepare2(maxCount, streamId);
    728         }
    729     }
    730 
    731     public void tearDown(Surface surface) throws CameraAccessException {
    732         if (surface == null) throw new IllegalArgumentException("Surface is null");
    733 
    734         synchronized(mInterfaceLock) {
    735             int streamId = -1;
    736             for (int i = 0; i < mConfiguredOutputs.size(); i++) {
    737                 if (surface == mConfiguredOutputs.valueAt(i).getSurface()) {
    738                     streamId = mConfiguredOutputs.keyAt(i);
    739                     break;
    740                 }
    741             }
    742             if (streamId == -1) {
    743                 throw new IllegalArgumentException("Surface is not part of this session");
    744             }
    745 
    746             mRemoteDevice.tearDown(streamId);
    747         }
    748     }
    749 
    750     public void finishDeferredConfig(List<OutputConfiguration> deferredConfigs)
    751             throws CameraAccessException {
    752         if (deferredConfigs == null || deferredConfigs.size() == 0) {
    753             throw new IllegalArgumentException("deferred config is null or empty");
    754         }
    755 
    756         synchronized(mInterfaceLock) {
    757             for (OutputConfiguration config : deferredConfigs) {
    758                 int streamId = -1;
    759                 for (int i = 0; i < mConfiguredOutputs.size(); i++) {
    760                     // Have to use equal here, as createCaptureSessionByOutputConfigurations() and
    761                     // createReprocessableCaptureSessionByConfigurations() do a copy of the configs.
    762                     if (config.equals(mConfiguredOutputs.valueAt(i))) {
    763                         streamId = mConfiguredOutputs.keyAt(i);
    764                         break;
    765                     }
    766                 }
    767                 if (streamId == -1) {
    768                     throw new IllegalArgumentException("Deferred config is not part of this "
    769                             + "session");
    770                 }
    771 
    772                 if (config.getSurface() == null) {
    773                     throw new IllegalArgumentException("The deferred config for stream " + streamId
    774                             + " must have a non-null surface");
    775                 }
    776                 mRemoteDevice.setDeferredConfiguration(streamId, config);
    777             }
    778         }
    779     }
    780 
    781     public int capture(CaptureRequest request, CaptureCallback callback, Handler handler)
    782             throws CameraAccessException {
    783         if (DEBUG) {
    784             Log.d(TAG, "calling capture");
    785         }
    786         List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
    787         requestList.add(request);
    788         return submitCaptureRequest(requestList, callback, handler, /*streaming*/false);
    789     }
    790 
    791     public int captureBurst(List<CaptureRequest> requests, CaptureCallback callback,
    792             Handler handler) throws CameraAccessException {
    793         if (requests == null || requests.isEmpty()) {
    794             throw new IllegalArgumentException("At least one request must be given");
    795         }
    796         return submitCaptureRequest(requests, callback, handler, /*streaming*/false);
    797     }
    798 
    799     /**
    800      * This method checks lastFrameNumber returned from ICameraDeviceUser methods for
    801      * starting and stopping repeating request and flushing.
    802      *
    803      * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never
    804      * sent to HAL. Then onCaptureSequenceAborted is immediately triggered.
    805      * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber as the last
    806      * regular frame number will be added to the list mRequestLastFrameNumbersList.</p>
    807      *
    808      * @param requestId the request ID of the current repeating request.
    809      *
    810      * @param lastFrameNumber last frame number returned from binder.
    811      */
    812     private void checkEarlyTriggerSequenceComplete(
    813             final int requestId, final long lastFrameNumber) {
    814         // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request
    815         // was never sent to HAL. Should trigger onCaptureSequenceAborted immediately.
    816         if (lastFrameNumber == CaptureCallback.NO_FRAMES_CAPTURED) {
    817             final CaptureCallbackHolder holder;
    818             int index = mCaptureCallbackMap.indexOfKey(requestId);
    819             holder = (index >= 0) ? mCaptureCallbackMap.valueAt(index) : null;
    820             if (holder != null) {
    821                 mCaptureCallbackMap.removeAt(index);
    822                 if (DEBUG) {
    823                     Log.v(TAG, String.format(
    824                             "remove holder for requestId %d, "
    825                             + "because lastFrame is %d.",
    826                             requestId, lastFrameNumber));
    827                 }
    828             }
    829 
    830             if (holder != null) {
    831                 if (DEBUG) {
    832                     Log.v(TAG, "immediately trigger onCaptureSequenceAborted because"
    833                             + " request did not reach HAL");
    834                 }
    835 
    836                 Runnable resultDispatch = new Runnable() {
    837                     @Override
    838                     public void run() {
    839                         if (!CameraDeviceImpl.this.isClosed()) {
    840                             if (DEBUG) {
    841                                 Log.d(TAG, String.format(
    842                                         "early trigger sequence complete for request %d",
    843                                         requestId));
    844                             }
    845                             holder.getCallback().onCaptureSequenceAborted(
    846                                     CameraDeviceImpl.this,
    847                                     requestId);
    848                         }
    849                     }
    850                 };
    851                 holder.getHandler().post(resultDispatch);
    852             } else {
    853                 Log.w(TAG, String.format(
    854                         "did not register callback to request %d",
    855                         requestId));
    856             }
    857         } else {
    858             // This function is only called for regular request so lastFrameNumber is the last
    859             // regular frame number.
    860             mRequestLastFrameNumbersList.add(new RequestLastFrameNumbersHolder(requestId,
    861                     lastFrameNumber));
    862 
    863             // It is possible that the last frame has already arrived, so we need to check
    864             // for sequence completion right away
    865             checkAndFireSequenceComplete();
    866         }
    867     }
    868 
    869     private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback,
    870             Handler handler, boolean repeating) throws CameraAccessException {
    871 
    872         // Need a valid handler, or current thread needs to have a looper, if
    873         // callback is valid
    874         handler = checkHandler(handler, callback);
    875 
    876         // Make sure that there all requests have at least 1 surface; all surfaces are non-null
    877         for (CaptureRequest request : requestList) {
    878             if (request.getTargets().isEmpty()) {
    879                 throw new IllegalArgumentException(
    880                         "Each request must have at least one Surface target");
    881             }
    882 
    883             for (Surface surface : request.getTargets()) {
    884                 if (surface == null) {
    885                     throw new IllegalArgumentException("Null Surface targets are not allowed");
    886                 }
    887             }
    888         }
    889 
    890         synchronized(mInterfaceLock) {
    891             checkIfCameraClosedOrInError();
    892             if (repeating) {
    893                 stopRepeating();
    894             }
    895 
    896             SubmitInfo requestInfo;
    897 
    898             CaptureRequest[] requestArray = requestList.toArray(new CaptureRequest[requestList.size()]);
    899             requestInfo = mRemoteDevice.submitRequestList(requestArray, repeating);
    900             if (DEBUG) {
    901                 Log.v(TAG, "last frame number " + requestInfo.getLastFrameNumber());
    902             }
    903 
    904             if (callback != null) {
    905                 mCaptureCallbackMap.put(requestInfo.getRequestId(),
    906                         new CaptureCallbackHolder(
    907                             callback, requestList, handler, repeating, mNextSessionId - 1));
    908             } else {
    909                 if (DEBUG) {
    910                     Log.d(TAG, "Listen for request " + requestInfo.getRequestId() + " is null");
    911                 }
    912             }
    913 
    914             if (repeating) {
    915                 if (mRepeatingRequestId != REQUEST_ID_NONE) {
    916                     checkEarlyTriggerSequenceComplete(mRepeatingRequestId,
    917                             requestInfo.getLastFrameNumber());
    918                 }
    919                 mRepeatingRequestId = requestInfo.getRequestId();
    920             } else {
    921                 mRequestLastFrameNumbersList.add(
    922                     new RequestLastFrameNumbersHolder(requestList, requestInfo));
    923             }
    924 
    925             if (mIdle) {
    926                 mDeviceHandler.post(mCallOnActive);
    927             }
    928             mIdle = false;
    929 
    930             return requestInfo.getRequestId();
    931         }
    932     }
    933 
    934     public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback,
    935             Handler handler) throws CameraAccessException {
    936         List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
    937         requestList.add(request);
    938         return submitCaptureRequest(requestList, callback, handler, /*streaming*/true);
    939     }
    940 
    941     public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback callback,
    942             Handler handler) throws CameraAccessException {
    943         if (requests == null || requests.isEmpty()) {
    944             throw new IllegalArgumentException("At least one request must be given");
    945         }
    946         return submitCaptureRequest(requests, callback, handler, /*streaming*/true);
    947     }
    948 
    949     public void stopRepeating() throws CameraAccessException {
    950 
    951         synchronized(mInterfaceLock) {
    952             checkIfCameraClosedOrInError();
    953             if (mRepeatingRequestId != REQUEST_ID_NONE) {
    954 
    955                 int requestId = mRepeatingRequestId;
    956                 mRepeatingRequestId = REQUEST_ID_NONE;
    957 
    958                 long lastFrameNumber;
    959                 try {
    960                     lastFrameNumber = mRemoteDevice.cancelRequest(requestId);
    961                 } catch (IllegalArgumentException e) {
    962                     if (DEBUG) {
    963                         Log.v(TAG, "Repeating request was already stopped for request " + requestId);
    964                     }
    965                     // Repeating request was already stopped. Nothing more to do.
    966                     return;
    967                 }
    968 
    969                 checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber);
    970             }
    971         }
    972     }
    973 
    974     private void waitUntilIdle() throws CameraAccessException {
    975 
    976         synchronized(mInterfaceLock) {
    977             checkIfCameraClosedOrInError();
    978 
    979             if (mRepeatingRequestId != REQUEST_ID_NONE) {
    980                 throw new IllegalStateException("Active repeating request ongoing");
    981             }
    982 
    983             mRemoteDevice.waitUntilIdle();
    984         }
    985     }
    986 
    987     public void flush() throws CameraAccessException {
    988         synchronized(mInterfaceLock) {
    989             checkIfCameraClosedOrInError();
    990 
    991             mDeviceHandler.post(mCallOnBusy);
    992 
    993             // If already idle, just do a busy->idle transition immediately, don't actually
    994             // flush.
    995             if (mIdle) {
    996                 mDeviceHandler.post(mCallOnIdle);
    997                 return;
    998             }
    999 
   1000             long lastFrameNumber = mRemoteDevice.flush();
   1001             if (mRepeatingRequestId != REQUEST_ID_NONE) {
   1002                 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
   1003                 mRepeatingRequestId = REQUEST_ID_NONE;
   1004             }
   1005         }
   1006     }
   1007 
   1008     @Override
   1009     public void close() {
   1010         synchronized (mInterfaceLock) {
   1011             if (mClosing.getAndSet(true)) {
   1012                 return;
   1013             }
   1014 
   1015             if (mRemoteDevice != null) {
   1016                 mRemoteDevice.disconnect();
   1017                 mRemoteDevice.unlinkToDeath(this, /*flags*/0);
   1018             }
   1019 
   1020             // Only want to fire the onClosed callback once;
   1021             // either a normal close where the remote device is valid
   1022             // or a close after a startup error (no remote device but in error state)
   1023             if (mRemoteDevice != null || mInError) {
   1024                 mDeviceHandler.post(mCallOnClosed);
   1025             }
   1026 
   1027             mRemoteDevice = null;
   1028         }
   1029     }
   1030 
   1031     @Override
   1032     protected void finalize() throws Throwable {
   1033         try {
   1034             close();
   1035         }
   1036         finally {
   1037             super.finalize();
   1038         }
   1039     }
   1040 
   1041     private void checkInputConfiguration(InputConfiguration inputConfig) {
   1042         if (inputConfig != null) {
   1043             StreamConfigurationMap configMap = mCharacteristics.get(
   1044                     CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
   1045 
   1046             int[] inputFormats = configMap.getInputFormats();
   1047             boolean validFormat = false;
   1048             for (int format : inputFormats) {
   1049                 if (format == inputConfig.getFormat()) {
   1050                     validFormat = true;
   1051                 }
   1052             }
   1053 
   1054             if (validFormat == false) {
   1055                 throw new IllegalArgumentException("input format " + inputConfig.getFormat() +
   1056                         " is not valid");
   1057             }
   1058 
   1059             boolean validSize = false;
   1060             Size[] inputSizes = configMap.getInputSizes(inputConfig.getFormat());
   1061             for (Size s : inputSizes) {
   1062                 if (inputConfig.getWidth() == s.getWidth() &&
   1063                         inputConfig.getHeight() == s.getHeight()) {
   1064                     validSize = true;
   1065                 }
   1066             }
   1067 
   1068             if (validSize == false) {
   1069                 throw new IllegalArgumentException("input size " + inputConfig.getWidth() + "x" +
   1070                         inputConfig.getHeight() + " is not valid");
   1071             }
   1072         }
   1073     }
   1074 
   1075     /**
   1076      * <p>A callback for tracking the progress of a {@link CaptureRequest}
   1077      * submitted to the camera device.</p>
   1078      *
   1079      */
   1080     public static abstract class CaptureCallback {
   1081 
   1082         /**
   1083          * This constant is used to indicate that no images were captured for
   1084          * the request.
   1085          *
   1086          * @hide
   1087          */
   1088         public static final int NO_FRAMES_CAPTURED = -1;
   1089 
   1090         /**
   1091          * This method is called when the camera device has started capturing
   1092          * the output image for the request, at the beginning of image exposure.
   1093          *
   1094          * @see android.media.MediaActionSound
   1095          */
   1096         public void onCaptureStarted(CameraDevice camera,
   1097                 CaptureRequest request, long timestamp, long frameNumber) {
   1098             // default empty implementation
   1099         }
   1100 
   1101         /**
   1102          * This method is called when some results from an image capture are
   1103          * available.
   1104          *
   1105          * @hide
   1106          */
   1107         public void onCapturePartial(CameraDevice camera,
   1108                 CaptureRequest request, CaptureResult result) {
   1109             // default empty implementation
   1110         }
   1111 
   1112         /**
   1113          * This method is called when an image capture makes partial forward progress; some
   1114          * (but not all) results from an image capture are available.
   1115          *
   1116          */
   1117         public void onCaptureProgressed(CameraDevice camera,
   1118                 CaptureRequest request, CaptureResult partialResult) {
   1119             // default empty implementation
   1120         }
   1121 
   1122         /**
   1123          * This method is called when an image capture has fully completed and all the
   1124          * result metadata is available.
   1125          */
   1126         public void onCaptureCompleted(CameraDevice camera,
   1127                 CaptureRequest request, TotalCaptureResult result) {
   1128             // default empty implementation
   1129         }
   1130 
   1131         /**
   1132          * This method is called instead of {@link #onCaptureCompleted} when the
   1133          * camera device failed to produce a {@link CaptureResult} for the
   1134          * request.
   1135          */
   1136         public void onCaptureFailed(CameraDevice camera,
   1137                 CaptureRequest request, CaptureFailure failure) {
   1138             // default empty implementation
   1139         }
   1140 
   1141         /**
   1142          * This method is called independently of the others in CaptureCallback,
   1143          * when a capture sequence finishes and all {@link CaptureResult}
   1144          * or {@link CaptureFailure} for it have been returned via this callback.
   1145          */
   1146         public void onCaptureSequenceCompleted(CameraDevice camera,
   1147                 int sequenceId, long frameNumber) {
   1148             // default empty implementation
   1149         }
   1150 
   1151         /**
   1152          * This method is called independently of the others in CaptureCallback,
   1153          * when a capture sequence aborts before any {@link CaptureResult}
   1154          * or {@link CaptureFailure} for it have been returned via this callback.
   1155          */
   1156         public void onCaptureSequenceAborted(CameraDevice camera,
   1157                 int sequenceId) {
   1158             // default empty implementation
   1159         }
   1160 
   1161         public void onCaptureBufferLost(CameraDevice camera,
   1162                 CaptureRequest request, Surface target, long frameNumber) {
   1163             // default empty implementation
   1164         }
   1165     }
   1166 
   1167     /**
   1168      * A callback for notifications about the state of a camera device, adding in the callbacks that
   1169      * were part of the earlier KK API design, but now only used internally.
   1170      */
   1171     public static abstract class StateCallbackKK extends StateCallback {
   1172         /**
   1173          * The method called when a camera device has no outputs configured.
   1174          *
   1175          */
   1176         public void onUnconfigured(CameraDevice camera) {
   1177             // Default empty implementation
   1178         }
   1179 
   1180         /**
   1181          * The method called when a camera device begins processing
   1182          * {@link CaptureRequest capture requests}.
   1183          *
   1184          */
   1185         public void onActive(CameraDevice camera) {
   1186             // Default empty implementation
   1187         }
   1188 
   1189         /**
   1190          * The method called when a camera device is busy.
   1191          *
   1192          */
   1193         public void onBusy(CameraDevice camera) {
   1194             // Default empty implementation
   1195         }
   1196 
   1197         /**
   1198          * The method called when a camera device has finished processing all
   1199          * submitted capture requests and has reached an idle state.
   1200          *
   1201          */
   1202         public void onIdle(CameraDevice camera) {
   1203             // Default empty implementation
   1204         }
   1205 
   1206         /**
   1207          * The method called when the camera device has finished preparing
   1208          * an output Surface
   1209          */
   1210         public void onSurfacePrepared(Surface surface) {
   1211             // Default empty implementation
   1212         }
   1213     }
   1214 
   1215     static class CaptureCallbackHolder {
   1216 
   1217         private final boolean mRepeating;
   1218         private final CaptureCallback mCallback;
   1219         private final List<CaptureRequest> mRequestList;
   1220         private final Handler mHandler;
   1221         private final int mSessionId;
   1222 
   1223         CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList,
   1224                 Handler handler, boolean repeating, int sessionId) {
   1225             if (callback == null || handler == null) {
   1226                 throw new UnsupportedOperationException(
   1227                     "Must have a valid handler and a valid callback");
   1228             }
   1229             mRepeating = repeating;
   1230             mHandler = handler;
   1231             mRequestList = new ArrayList<CaptureRequest>(requestList);
   1232             mCallback = callback;
   1233             mSessionId = sessionId;
   1234         }
   1235 
   1236         public boolean isRepeating() {
   1237             return mRepeating;
   1238         }
   1239 
   1240         public CaptureCallback getCallback() {
   1241             return mCallback;
   1242         }
   1243 
   1244         public CaptureRequest getRequest(int subsequenceId) {
   1245             if (subsequenceId >= mRequestList.size()) {
   1246                 throw new IllegalArgumentException(
   1247                         String.format(
   1248                                 "Requested subsequenceId %d is larger than request list size %d.",
   1249                                 subsequenceId, mRequestList.size()));
   1250             } else {
   1251                 if (subsequenceId < 0) {
   1252                     throw new IllegalArgumentException(String.format(
   1253                             "Requested subsequenceId %d is negative", subsequenceId));
   1254                 } else {
   1255                     return mRequestList.get(subsequenceId);
   1256                 }
   1257             }
   1258         }
   1259 
   1260         public CaptureRequest getRequest() {
   1261             return getRequest(0);
   1262         }
   1263 
   1264         public Handler getHandler() {
   1265             return mHandler;
   1266         }
   1267 
   1268         public int getSessionId() {
   1269             return mSessionId;
   1270         }
   1271     }
   1272 
   1273     /**
   1274      * This class holds a capture ID and its expected last regular frame number and last reprocess
   1275      * frame number.
   1276      */
   1277     static class RequestLastFrameNumbersHolder {
   1278         // request ID
   1279         private final int mRequestId;
   1280         // The last regular frame number for this request ID. It's
   1281         // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no regular request.
   1282         private final long mLastRegularFrameNumber;
   1283         // The last reprocess frame number for this request ID. It's
   1284         // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no reprocess request.
   1285         private final long mLastReprocessFrameNumber;
   1286 
   1287         /**
   1288          * Create a request-last-frame-numbers holder with a list of requests, request ID, and
   1289          * the last frame number returned by camera service.
   1290          */
   1291         public RequestLastFrameNumbersHolder(List<CaptureRequest> requestList, SubmitInfo requestInfo) {
   1292             long lastRegularFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
   1293             long lastReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
   1294             long frameNumber = requestInfo.getLastFrameNumber();
   1295 
   1296             if (requestInfo.getLastFrameNumber() < requestList.size() - 1) {
   1297                 throw new IllegalArgumentException(
   1298                         "lastFrameNumber: " + requestInfo.getLastFrameNumber() +
   1299                         " should be at least " + (requestList.size() - 1) + " for the number of " +
   1300                         " requests in the list: " + requestList.size());
   1301             }
   1302 
   1303             // find the last regular frame number and the last reprocess frame number
   1304             for (int i = requestList.size() - 1; i >= 0; i--) {
   1305                 CaptureRequest request = requestList.get(i);
   1306                 if (request.isReprocess() && lastReprocessFrameNumber ==
   1307                         CaptureCallback.NO_FRAMES_CAPTURED) {
   1308                     lastReprocessFrameNumber = frameNumber;
   1309                 } else if (!request.isReprocess() && lastRegularFrameNumber ==
   1310                         CaptureCallback.NO_FRAMES_CAPTURED) {
   1311                     lastRegularFrameNumber = frameNumber;
   1312                 }
   1313 
   1314                 if (lastReprocessFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED &&
   1315                         lastRegularFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED) {
   1316                     break;
   1317                 }
   1318 
   1319                 frameNumber--;
   1320             }
   1321 
   1322             mLastRegularFrameNumber = lastRegularFrameNumber;
   1323             mLastReprocessFrameNumber = lastReprocessFrameNumber;
   1324             mRequestId = requestInfo.getRequestId();
   1325         }
   1326 
   1327         /**
   1328          * Create a request-last-frame-numbers holder with a request ID and last regular frame
   1329          * number.
   1330          */
   1331         public RequestLastFrameNumbersHolder(int requestId, long lastRegularFrameNumber) {
   1332             mLastRegularFrameNumber = lastRegularFrameNumber;
   1333             mLastReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
   1334             mRequestId = requestId;
   1335         }
   1336 
   1337         /**
   1338          * Return the last regular frame number. Return CaptureCallback.NO_FRAMES_CAPTURED if
   1339          * it contains no regular request.
   1340          */
   1341         public long getLastRegularFrameNumber() {
   1342             return mLastRegularFrameNumber;
   1343         }
   1344 
   1345         /**
   1346          * Return the last reprocess frame number. Return CaptureCallback.NO_FRAMES_CAPTURED if
   1347          * it contains no reprocess request.
   1348          */
   1349         public long getLastReprocessFrameNumber() {
   1350             return mLastReprocessFrameNumber;
   1351         }
   1352 
   1353         /**
   1354          * Return the last frame number overall.
   1355          */
   1356         public long getLastFrameNumber() {
   1357             return Math.max(mLastRegularFrameNumber, mLastReprocessFrameNumber);
   1358         }
   1359 
   1360         /**
   1361          * Return the request ID.
   1362          */
   1363         public int getRequestId() {
   1364             return mRequestId;
   1365         }
   1366     }
   1367 
   1368     /**
   1369      * This class tracks the last frame number for submitted requests.
   1370      */
   1371     public class FrameNumberTracker {
   1372 
   1373         private long mCompletedFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
   1374         private long mCompletedReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
   1375         /** the skipped frame numbers that belong to regular results */
   1376         private final LinkedList<Long> mSkippedRegularFrameNumbers = new LinkedList<Long>();
   1377         /** the skipped frame numbers that belong to reprocess results */
   1378         private final LinkedList<Long> mSkippedReprocessFrameNumbers = new LinkedList<Long>();
   1379         /** frame number -> is reprocess */
   1380         private final TreeMap<Long, Boolean> mFutureErrorMap = new TreeMap<Long, Boolean>();
   1381         /** Map frame numbers to list of partial results */
   1382         private final HashMap<Long, List<CaptureResult>> mPartialResults = new HashMap<>();
   1383 
   1384         private void update() {
   1385             Iterator iter = mFutureErrorMap.entrySet().iterator();
   1386             while (iter.hasNext()) {
   1387                 TreeMap.Entry pair = (TreeMap.Entry)iter.next();
   1388                 Long errorFrameNumber = (Long)pair.getKey();
   1389                 Boolean reprocess = (Boolean)pair.getValue();
   1390                 Boolean removeError = true;
   1391                 if (reprocess) {
   1392                     if (errorFrameNumber == mCompletedReprocessFrameNumber + 1) {
   1393                         mCompletedReprocessFrameNumber = errorFrameNumber;
   1394                     } else if (mSkippedReprocessFrameNumbers.isEmpty() != true &&
   1395                             errorFrameNumber == mSkippedReprocessFrameNumbers.element()) {
   1396                         mCompletedReprocessFrameNumber = errorFrameNumber;
   1397                         mSkippedReprocessFrameNumbers.remove();
   1398                     } else {
   1399                         removeError = false;
   1400                     }
   1401                 } else {
   1402                     if (errorFrameNumber == mCompletedFrameNumber + 1) {
   1403                         mCompletedFrameNumber = errorFrameNumber;
   1404                     } else if (mSkippedRegularFrameNumbers.isEmpty() != true &&
   1405                             errorFrameNumber == mSkippedRegularFrameNumbers.element()) {
   1406                         mCompletedFrameNumber = errorFrameNumber;
   1407                         mSkippedRegularFrameNumbers.remove();
   1408                     } else {
   1409                         removeError = false;
   1410                     }
   1411                 }
   1412                 if (removeError) {
   1413                     iter.remove();
   1414                 }
   1415             }
   1416         }
   1417 
   1418         /**
   1419          * This function is called every time when a result or an error is received.
   1420          * @param frameNumber the frame number corresponding to the result or error
   1421          * @param isError true if it is an error, false if it is not an error
   1422          * @param isReprocess true if it is a reprocess result, false if it is a regular result.
   1423          */
   1424         public void updateTracker(long frameNumber, boolean isError, boolean isReprocess) {
   1425             if (isError) {
   1426                 mFutureErrorMap.put(frameNumber, isReprocess);
   1427             } else {
   1428                 try {
   1429                     if (isReprocess) {
   1430                         updateCompletedReprocessFrameNumber(frameNumber);
   1431                     } else {
   1432                         updateCompletedFrameNumber(frameNumber);
   1433                     }
   1434                 } catch (IllegalArgumentException e) {
   1435                     Log.e(TAG, e.getMessage());
   1436                 }
   1437             }
   1438             update();
   1439         }
   1440 
   1441         /**
   1442          * This function is called every time a result has been completed.
   1443          *
   1444          * <p>It keeps a track of all the partial results already created for a particular
   1445          * frame number.</p>
   1446          *
   1447          * @param frameNumber the frame number corresponding to the result
   1448          * @param result the total or partial result
   1449          * @param partial {@true} if the result is partial, {@code false} if total
   1450          * @param isReprocess true if it is a reprocess result, false if it is a regular result.
   1451          */
   1452         public void updateTracker(long frameNumber, CaptureResult result, boolean partial,
   1453                 boolean isReprocess) {
   1454             if (!partial) {
   1455                 // Update the total result's frame status as being successful
   1456                 updateTracker(frameNumber, /*isError*/false, isReprocess);
   1457                 // Don't keep a list of total results, we don't need to track them
   1458                 return;
   1459             }
   1460 
   1461             if (result == null) {
   1462                 // Do not record blank results; this also means there will be no total result
   1463                 // so it doesn't matter that the partials were not recorded
   1464                 return;
   1465             }
   1466 
   1467             // Partial results must be aggregated in-order for that frame number
   1468             List<CaptureResult> partials = mPartialResults.get(frameNumber);
   1469             if (partials == null) {
   1470                 partials = new ArrayList<>();
   1471                 mPartialResults.put(frameNumber, partials);
   1472             }
   1473 
   1474             partials.add(result);
   1475         }
   1476 
   1477         /**
   1478          * Attempt to pop off all of the partial results seen so far for the {@code frameNumber}.
   1479          *
   1480          * <p>Once popped-off, the partial results are forgotten (unless {@code updateTracker}
   1481          * is called again with new partials for that frame number).</p>
   1482          *
   1483          * @param frameNumber the frame number corresponding to the result
   1484          * @return a list of partial results for that frame with at least 1 element,
   1485          *         or {@code null} if there were no partials recorded for that frame
   1486          */
   1487         public List<CaptureResult> popPartialResults(long frameNumber) {
   1488             return mPartialResults.remove(frameNumber);
   1489         }
   1490 
   1491         public long getCompletedFrameNumber() {
   1492             return mCompletedFrameNumber;
   1493         }
   1494 
   1495         public long getCompletedReprocessFrameNumber() {
   1496             return mCompletedReprocessFrameNumber;
   1497         }
   1498 
   1499         /**
   1500          * Update the completed frame number for regular results.
   1501          *
   1502          * It validates that all previous frames have arrived except for reprocess frames.
   1503          *
   1504          * If there is a gap since previous regular frame number, assume the frames in the gap are
   1505          * reprocess frames and store them in the skipped reprocess frame number queue to check
   1506          * against when reprocess frames arrive.
   1507          */
   1508         private void updateCompletedFrameNumber(long frameNumber) throws IllegalArgumentException {
   1509             if (frameNumber <= mCompletedFrameNumber) {
   1510                 throw new IllegalArgumentException("frame number " + frameNumber + " is a repeat");
   1511             } else if (frameNumber <= mCompletedReprocessFrameNumber) {
   1512                 // if frame number is smaller than completed reprocess frame number,
   1513                 // it must be the head of mSkippedRegularFrameNumbers
   1514                 if (mSkippedRegularFrameNumbers.isEmpty() == true ||
   1515                         frameNumber < mSkippedRegularFrameNumbers.element()) {
   1516                     throw new IllegalArgumentException("frame number " + frameNumber +
   1517                             " is a repeat");
   1518                 } else if (frameNumber > mSkippedRegularFrameNumbers.element()) {
   1519                     throw new IllegalArgumentException("frame number " + frameNumber +
   1520                             " comes out of order. Expecting " +
   1521                             mSkippedRegularFrameNumbers.element());
   1522                 }
   1523                 // frame number matches the head of the skipped frame number queue.
   1524                 mSkippedRegularFrameNumbers.remove();
   1525             } else {
   1526                 // there is a gap of unseen frame numbers which should belong to reprocess result
   1527                 // put all the skipped frame numbers in the queue
   1528                 for (long i = Math.max(mCompletedFrameNumber, mCompletedReprocessFrameNumber) + 1;
   1529                         i < frameNumber; i++) {
   1530                     mSkippedReprocessFrameNumbers.add(i);
   1531                 }
   1532             }
   1533 
   1534             mCompletedFrameNumber = frameNumber;
   1535         }
   1536 
   1537         /**
   1538          * Update the completed frame number for reprocess results.
   1539          *
   1540          * It validates that all previous frames have arrived except for regular frames.
   1541          *
   1542          * If there is a gap since previous reprocess frame number, assume the frames in the gap are
   1543          * regular frames and store them in the skipped regular frame number queue to check
   1544          * against when regular frames arrive.
   1545          */
   1546         private void updateCompletedReprocessFrameNumber(long frameNumber)
   1547                 throws IllegalArgumentException {
   1548             if (frameNumber < mCompletedReprocessFrameNumber) {
   1549                 throw new IllegalArgumentException("frame number " + frameNumber + " is a repeat");
   1550             } else if (frameNumber < mCompletedFrameNumber) {
   1551                 // if reprocess frame number is smaller than completed regular frame number,
   1552                 // it must be the head of the skipped reprocess frame number queue.
   1553                 if (mSkippedReprocessFrameNumbers.isEmpty() == true ||
   1554                         frameNumber < mSkippedReprocessFrameNumbers.element()) {
   1555                     throw new IllegalArgumentException("frame number " + frameNumber +
   1556                             " is a repeat");
   1557                 } else if (frameNumber > mSkippedReprocessFrameNumbers.element()) {
   1558                     throw new IllegalArgumentException("frame number " + frameNumber +
   1559                             " comes out of order. Expecting " +
   1560                             mSkippedReprocessFrameNumbers.element());
   1561                 }
   1562                 // frame number matches the head of the skipped frame number queue.
   1563                 mSkippedReprocessFrameNumbers.remove();
   1564             } else {
   1565                 // put all the skipped frame numbers in the queue
   1566                 for (long i = Math.max(mCompletedFrameNumber, mCompletedReprocessFrameNumber) + 1;
   1567                         i < frameNumber; i++) {
   1568                     mSkippedRegularFrameNumbers.add(i);
   1569                 }
   1570             }
   1571             mCompletedReprocessFrameNumber = frameNumber;
   1572         }
   1573     }
   1574 
   1575     private void checkAndFireSequenceComplete() {
   1576         long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
   1577         long completedReprocessFrameNumber = mFrameNumberTracker.getCompletedReprocessFrameNumber();
   1578         boolean isReprocess = false;
   1579         Iterator<RequestLastFrameNumbersHolder> iter = mRequestLastFrameNumbersList.iterator();
   1580         while (iter.hasNext()) {
   1581             final RequestLastFrameNumbersHolder requestLastFrameNumbers = iter.next();
   1582             boolean sequenceCompleted = false;
   1583             final int requestId = requestLastFrameNumbers.getRequestId();
   1584             final CaptureCallbackHolder holder;
   1585             synchronized(mInterfaceLock) {
   1586                 if (mRemoteDevice == null) {
   1587                     Log.w(TAG, "Camera closed while checking sequences");
   1588                     return;
   1589                 }
   1590 
   1591                 int index = mCaptureCallbackMap.indexOfKey(requestId);
   1592                 holder = (index >= 0) ?
   1593                         mCaptureCallbackMap.valueAt(index) : null;
   1594                 if (holder != null) {
   1595                     long lastRegularFrameNumber =
   1596                             requestLastFrameNumbers.getLastRegularFrameNumber();
   1597                     long lastReprocessFrameNumber =
   1598                             requestLastFrameNumbers.getLastReprocessFrameNumber();
   1599 
   1600                     // check if it's okay to remove request from mCaptureCallbackMap
   1601                     if (lastRegularFrameNumber <= completedFrameNumber &&
   1602                             lastReprocessFrameNumber <= completedReprocessFrameNumber) {
   1603                         sequenceCompleted = true;
   1604                         mCaptureCallbackMap.removeAt(index);
   1605                         if (DEBUG) {
   1606                             Log.v(TAG, String.format(
   1607                                     "Remove holder for requestId %d, because lastRegularFrame %d " +
   1608                                     "is <= %d and lastReprocessFrame %d is <= %d", requestId,
   1609                                     lastRegularFrameNumber, completedFrameNumber,
   1610                                     lastReprocessFrameNumber, completedReprocessFrameNumber));
   1611                         }
   1612                     }
   1613                 }
   1614             }
   1615 
   1616             // If no callback is registered for this requestId or sequence completed, remove it
   1617             // from the frame number->request pair because it's not needed anymore.
   1618             if (holder == null || sequenceCompleted) {
   1619                 iter.remove();
   1620             }
   1621 
   1622             // Call onCaptureSequenceCompleted
   1623             if (sequenceCompleted) {
   1624                 Runnable resultDispatch = new Runnable() {
   1625                     @Override
   1626                     public void run() {
   1627                         if (!CameraDeviceImpl.this.isClosed()){
   1628                             if (DEBUG) {
   1629                                 Log.d(TAG, String.format(
   1630                                         "fire sequence complete for request %d",
   1631                                         requestId));
   1632                             }
   1633 
   1634                             holder.getCallback().onCaptureSequenceCompleted(
   1635                                 CameraDeviceImpl.this,
   1636                                 requestId,
   1637                                 requestLastFrameNumbers.getLastFrameNumber());
   1638                         }
   1639                     }
   1640                 };
   1641                 holder.getHandler().post(resultDispatch);
   1642             }
   1643         }
   1644     }
   1645 
   1646     public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
   1647 
   1648         @Override
   1649         public IBinder asBinder() {
   1650             return this;
   1651         }
   1652 
   1653         @Override
   1654         public void onDeviceError(final int errorCode, CaptureResultExtras resultExtras) {
   1655             if (DEBUG) {
   1656                 Log.d(TAG, String.format(
   1657                         "Device error received, code %d, frame number %d, request ID %d, subseq ID %d",
   1658                         errorCode, resultExtras.getFrameNumber(), resultExtras.getRequestId(),
   1659                         resultExtras.getSubsequenceId()));
   1660             }
   1661 
   1662             synchronized(mInterfaceLock) {
   1663                 if (mRemoteDevice == null) {
   1664                     return; // Camera already closed
   1665                 }
   1666 
   1667                 switch (errorCode) {
   1668                     case ERROR_CAMERA_DISCONNECTED:
   1669                         CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected);
   1670                         break;
   1671                     default:
   1672                         Log.e(TAG, "Unknown error from camera device: " + errorCode);
   1673                         // no break
   1674                     case ERROR_CAMERA_DEVICE:
   1675                     case ERROR_CAMERA_SERVICE:
   1676                         mInError = true;
   1677                         final int publicErrorCode = (errorCode == ERROR_CAMERA_DEVICE) ?
   1678                                 StateCallback.ERROR_CAMERA_DEVICE :
   1679                                 StateCallback.ERROR_CAMERA_SERVICE;
   1680                         Runnable r = new Runnable() {
   1681                             @Override
   1682                             public void run() {
   1683                                 if (!CameraDeviceImpl.this.isClosed()) {
   1684                                     mDeviceCallback.onError(CameraDeviceImpl.this, publicErrorCode);
   1685                                 }
   1686                             }
   1687                         };
   1688                         CameraDeviceImpl.this.mDeviceHandler.post(r);
   1689                         break;
   1690                     case ERROR_CAMERA_REQUEST:
   1691                     case ERROR_CAMERA_RESULT:
   1692                     case ERROR_CAMERA_BUFFER:
   1693                         onCaptureErrorLocked(errorCode, resultExtras);
   1694                         break;
   1695                 }
   1696             }
   1697         }
   1698 
   1699         @Override
   1700         public void onRepeatingRequestError(long lastFrameNumber) {
   1701             if (DEBUG) {
   1702                 Log.d(TAG, "Repeating request error received. Last frame number is " +
   1703                         lastFrameNumber);
   1704             }
   1705 
   1706             synchronized(mInterfaceLock) {
   1707                 // Camera is already closed or no repeating request is present.
   1708                 if (mRemoteDevice == null || mRepeatingRequestId == REQUEST_ID_NONE) {
   1709                     return; // Camera already closed
   1710                 }
   1711 
   1712                 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
   1713                 mRepeatingRequestId = REQUEST_ID_NONE;
   1714             }
   1715         }
   1716 
   1717         @Override
   1718         public void onDeviceIdle() {
   1719             if (DEBUG) {
   1720                 Log.d(TAG, "Camera now idle");
   1721             }
   1722             synchronized(mInterfaceLock) {
   1723                 if (mRemoteDevice == null) return; // Camera already closed
   1724 
   1725                 if (!CameraDeviceImpl.this.mIdle) {
   1726                     CameraDeviceImpl.this.mDeviceHandler.post(mCallOnIdle);
   1727                 }
   1728                 CameraDeviceImpl.this.mIdle = true;
   1729             }
   1730         }
   1731 
   1732         @Override
   1733         public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) {
   1734             int requestId = resultExtras.getRequestId();
   1735             final long frameNumber = resultExtras.getFrameNumber();
   1736 
   1737             if (DEBUG) {
   1738                 Log.d(TAG, "Capture started for id " + requestId + " frame number " + frameNumber);
   1739             }
   1740             final CaptureCallbackHolder holder;
   1741 
   1742             synchronized(mInterfaceLock) {
   1743                 if (mRemoteDevice == null) return; // Camera already closed
   1744 
   1745                 // Get the callback for this frame ID, if there is one
   1746                 holder = CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId);
   1747 
   1748                 if (holder == null) {
   1749                     return;
   1750                 }
   1751 
   1752                 if (isClosed()) return;
   1753 
   1754                 // Dispatch capture start notice
   1755                 holder.getHandler().post(
   1756                     new Runnable() {
   1757                         @Override
   1758                         public void run() {
   1759                             if (!CameraDeviceImpl.this.isClosed()) {
   1760                                 holder.getCallback().onCaptureStarted(
   1761                                     CameraDeviceImpl.this,
   1762                                     holder.getRequest(resultExtras.getSubsequenceId()),
   1763                                     timestamp, frameNumber);
   1764                             }
   1765                         }
   1766                     });
   1767 
   1768             }
   1769         }
   1770 
   1771         @Override
   1772         public void onResultReceived(CameraMetadataNative result,
   1773                 CaptureResultExtras resultExtras) throws RemoteException {
   1774 
   1775             int requestId = resultExtras.getRequestId();
   1776             long frameNumber = resultExtras.getFrameNumber();
   1777 
   1778             if (DEBUG) {
   1779                 Log.v(TAG, "Received result frame " + frameNumber + " for id "
   1780                         + requestId);
   1781             }
   1782 
   1783             synchronized(mInterfaceLock) {
   1784                 if (mRemoteDevice == null) return; // Camera already closed
   1785 
   1786                 // TODO: Handle CameraCharacteristics access from CaptureResult correctly.
   1787                 result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE,
   1788                         getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE));
   1789 
   1790                 final CaptureCallbackHolder holder =
   1791                         CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId);
   1792                 final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
   1793 
   1794                 boolean isPartialResult =
   1795                         (resultExtras.getPartialResultCount() < mTotalPartialCount);
   1796                 boolean isReprocess = request.isReprocess();
   1797 
   1798                 // Check if we have a callback for this
   1799                 if (holder == null) {
   1800                     if (DEBUG) {
   1801                         Log.d(TAG,
   1802                                 "holder is null, early return at frame "
   1803                                         + frameNumber);
   1804                     }
   1805 
   1806                     mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult,
   1807                             isReprocess);
   1808 
   1809                     return;
   1810                 }
   1811 
   1812                 if (isClosed()) {
   1813                     if (DEBUG) {
   1814                         Log.d(TAG,
   1815                                 "camera is closed, early return at frame "
   1816                                         + frameNumber);
   1817                     }
   1818 
   1819                     mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult,
   1820                             isReprocess);
   1821                     return;
   1822                 }
   1823 
   1824 
   1825                 Runnable resultDispatch = null;
   1826 
   1827                 CaptureResult finalResult;
   1828 
   1829                 // Either send a partial result or the final capture completed result
   1830                 if (isPartialResult) {
   1831                     final CaptureResult resultAsCapture =
   1832                             new CaptureResult(result, request, resultExtras);
   1833 
   1834                     // Partial result
   1835                     resultDispatch = new Runnable() {
   1836                         @Override
   1837                         public void run() {
   1838                             if (!CameraDeviceImpl.this.isClosed()){
   1839                                 holder.getCallback().onCaptureProgressed(
   1840                                     CameraDeviceImpl.this,
   1841                                     request,
   1842                                     resultAsCapture);
   1843                             }
   1844                         }
   1845                     };
   1846 
   1847                     finalResult = resultAsCapture;
   1848                 } else {
   1849                     List<CaptureResult> partialResults =
   1850                             mFrameNumberTracker.popPartialResults(frameNumber);
   1851 
   1852                     final TotalCaptureResult resultAsCapture = new TotalCaptureResult(result,
   1853                             request, resultExtras, partialResults, holder.getSessionId());
   1854 
   1855                     // Final capture result
   1856                     resultDispatch = new Runnable() {
   1857                         @Override
   1858                         public void run() {
   1859                             if (!CameraDeviceImpl.this.isClosed()){
   1860                                 holder.getCallback().onCaptureCompleted(
   1861                                     CameraDeviceImpl.this,
   1862                                     request,
   1863                                     resultAsCapture);
   1864                             }
   1865                         }
   1866                     };
   1867 
   1868                     finalResult = resultAsCapture;
   1869                 }
   1870 
   1871                 holder.getHandler().post(resultDispatch);
   1872 
   1873                 // Collect the partials for a total result; or mark the frame as totally completed
   1874                 mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult,
   1875                         isReprocess);
   1876 
   1877                 // Fire onCaptureSequenceCompleted
   1878                 if (!isPartialResult) {
   1879                     checkAndFireSequenceComplete();
   1880                 }
   1881             }
   1882         }
   1883 
   1884         @Override
   1885         public void onPrepared(int streamId) {
   1886             final OutputConfiguration output;
   1887             final StateCallbackKK sessionCallback;
   1888 
   1889             if (DEBUG) {
   1890                 Log.v(TAG, "Stream " + streamId + " is prepared");
   1891             }
   1892 
   1893             synchronized(mInterfaceLock) {
   1894                 output = mConfiguredOutputs.get(streamId);
   1895                 sessionCallback = mSessionStateCallback;
   1896             }
   1897 
   1898             if (sessionCallback == null) return;
   1899 
   1900             if (output == null) {
   1901                 Log.w(TAG, "onPrepared invoked for unknown output Surface");
   1902                 return;
   1903             }
   1904             final Surface surface = output.getSurface();
   1905 
   1906             sessionCallback.onSurfacePrepared(surface);
   1907         }
   1908 
   1909         /**
   1910          * Called by onDeviceError for handling single-capture failures.
   1911          */
   1912         private void onCaptureErrorLocked(int errorCode, CaptureResultExtras resultExtras) {
   1913 
   1914             final int requestId = resultExtras.getRequestId();
   1915             final int subsequenceId = resultExtras.getSubsequenceId();
   1916             final long frameNumber = resultExtras.getFrameNumber();
   1917             final CaptureCallbackHolder holder =
   1918                     CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId);
   1919 
   1920             final CaptureRequest request = holder.getRequest(subsequenceId);
   1921 
   1922             Runnable failureDispatch = null;
   1923             if (errorCode == ERROR_CAMERA_BUFFER) {
   1924                 final Surface outputSurface =
   1925                         mConfiguredOutputs.get(resultExtras.getErrorStreamId()).getSurface();
   1926                 if (DEBUG) {
   1927                     Log.v(TAG, String.format("Lost output buffer reported for frame %d, target %s",
   1928                             frameNumber, outputSurface));
   1929                 }
   1930                 failureDispatch = new Runnable() {
   1931                     @Override
   1932                     public void run() {
   1933                         if (!CameraDeviceImpl.this.isClosed()){
   1934                             holder.getCallback().onCaptureBufferLost(
   1935                                 CameraDeviceImpl.this,
   1936                                 request,
   1937                                 outputSurface,
   1938                                 frameNumber);
   1939                         }
   1940                     }
   1941                 };
   1942             } else {
   1943                 boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT);
   1944 
   1945                 // This is only approximate - exact handling needs the camera service and HAL to
   1946                 // disambiguate between request failures to due abort and due to real errors.  For
   1947                 // now, assume that if the session believes we're mid-abort, then the error is due
   1948                 // to abort.
   1949                 int reason = (mCurrentSession != null && mCurrentSession.isAborting()) ?
   1950                         CaptureFailure.REASON_FLUSHED :
   1951                         CaptureFailure.REASON_ERROR;
   1952 
   1953                 final CaptureFailure failure = new CaptureFailure(
   1954                     request,
   1955                     reason,
   1956                     /*dropped*/ mayHaveBuffers,
   1957                     requestId,
   1958                     frameNumber);
   1959 
   1960                 failureDispatch = new Runnable() {
   1961                     @Override
   1962                     public void run() {
   1963                         if (!CameraDeviceImpl.this.isClosed()){
   1964                             holder.getCallback().onCaptureFailed(
   1965                                 CameraDeviceImpl.this,
   1966                                 request,
   1967                                 failure);
   1968                         }
   1969                     }
   1970                 };
   1971 
   1972                 // Fire onCaptureSequenceCompleted if appropriate
   1973                 if (DEBUG) {
   1974                     Log.v(TAG, String.format("got error frame %d", frameNumber));
   1975                 }
   1976                 mFrameNumberTracker.updateTracker(frameNumber, /*error*/true, request.isReprocess());
   1977                 checkAndFireSequenceComplete();
   1978             }
   1979 
   1980             // Dispatch the failure callback
   1981             holder.getHandler().post(failureDispatch);
   1982         }
   1983 
   1984     } // public class CameraDeviceCallbacks
   1985 
   1986     /**
   1987      * Default handler management.
   1988      *
   1989      * <p>
   1990      * If handler is null, get the current thread's
   1991      * Looper to create a Handler with. If no looper exists, throw {@code IllegalArgumentException}.
   1992      * </p>
   1993      */
   1994     static Handler checkHandler(Handler handler) {
   1995         if (handler == null) {
   1996             Looper looper = Looper.myLooper();
   1997             if (looper == null) {
   1998                 throw new IllegalArgumentException(
   1999                     "No handler given, and current thread has no looper!");
   2000             }
   2001             handler = new Handler(looper);
   2002         }
   2003         return handler;
   2004     }
   2005 
   2006     /**
   2007      * Default handler management, conditional on there being a callback.
   2008      *
   2009      * <p>If the callback isn't null, check the handler, otherwise pass it through.</p>
   2010      */
   2011     static <T> Handler checkHandler(Handler handler, T callback) {
   2012         if (callback != null) {
   2013             return checkHandler(handler);
   2014         }
   2015         return handler;
   2016     }
   2017 
   2018     private void checkIfCameraClosedOrInError() throws CameraAccessException {
   2019         if (mRemoteDevice == null) {
   2020             throw new IllegalStateException("CameraDevice was already closed");
   2021         }
   2022         if (mInError) {
   2023             throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
   2024                     "The camera device has encountered a serious error");
   2025         }
   2026     }
   2027 
   2028     /** Whether the camera device has started to close (may not yet have finished) */
   2029     private boolean isClosed() {
   2030         return mClosing.get();
   2031     }
   2032 
   2033     private CameraCharacteristics getCharacteristics() {
   2034         return mCharacteristics;
   2035     }
   2036 
   2037     /**
   2038      * Listener for binder death.
   2039      *
   2040      * <p> Handle binder death for ICameraDeviceUser. Trigger onError.</p>
   2041      */
   2042     @Override
   2043     public void binderDied() {
   2044         Log.w(TAG, "CameraDevice " + mCameraId + " died unexpectedly");
   2045 
   2046         if (mRemoteDevice == null) {
   2047             return; // Camera already closed
   2048         }
   2049 
   2050         mInError = true;
   2051         Runnable r = new Runnable() {
   2052             @Override
   2053             public void run() {
   2054                 if (!isClosed()) {
   2055                     mDeviceCallback.onError(CameraDeviceImpl.this,
   2056                             StateCallback.ERROR_CAMERA_SERVICE);
   2057                 }
   2058             }
   2059         };
   2060         CameraDeviceImpl.this.mDeviceHandler.post(r);
   2061     }
   2062 }
   2063