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