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