Home | History | Annotate | Download | only in impl
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.hardware.camera2.impl;
     18 
     19 import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
     20 
     21 import android.hardware.camera2.CameraAccessException;
     22 import android.hardware.camera2.CameraCaptureSession;
     23 import android.hardware.camera2.CameraCharacteristics;
     24 import android.hardware.camera2.CameraDevice;
     25 import android.hardware.camera2.CaptureRequest;
     26 import android.hardware.camera2.CaptureResult;
     27 import android.hardware.camera2.CaptureFailure;
     28 import android.hardware.camera2.ICameraDeviceCallbacks;
     29 import android.hardware.camera2.ICameraDeviceUser;
     30 import android.hardware.camera2.TotalCaptureResult;
     31 import android.hardware.camera2.utils.CameraBinderDecorator;
     32 import android.hardware.camera2.utils.CameraRuntimeException;
     33 import android.hardware.camera2.utils.LongParcelable;
     34 import android.os.Handler;
     35 import android.os.IBinder;
     36 import android.os.Looper;
     37 import android.os.RemoteException;
     38 import android.util.Log;
     39 import android.util.SparseArray;
     40 import android.view.Surface;
     41 
     42 import java.util.AbstractMap.SimpleEntry;
     43 import java.util.ArrayList;
     44 import java.util.HashMap;
     45 import java.util.HashSet;
     46 import java.util.Iterator;
     47 import java.util.List;
     48 import java.util.TreeSet;
     49 
     50 /**
     51  * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
     52  */
     53 public class CameraDeviceImpl extends CameraDevice {
     54     private final String TAG;
     55     private final boolean DEBUG;
     56 
     57     private static final int REQUEST_ID_NONE = -1;
     58 
     59     // TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
     60     private ICameraDeviceUser mRemoteDevice;
     61 
     62     // Lock to synchronize cross-thread access to device public interface
     63     final Object mInterfaceLock = new Object(); // access from this class and Session only!
     64     private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
     65 
     66     private final StateCallback mDeviceCallback;
     67     private volatile StateCallbackKK mSessionStateCallback;
     68     private final Handler mDeviceHandler;
     69 
     70     private volatile boolean mClosing = false;
     71     private boolean mInError = false;
     72     private boolean mIdle = true;
     73 
     74     /** map request IDs to callback/request data */
     75     private final SparseArray<CaptureCallbackHolder> mCaptureCallbackMap =
     76             new SparseArray<CaptureCallbackHolder>();
     77 
     78     private int mRepeatingRequestId = REQUEST_ID_NONE;
     79     private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>();
     80     // Map stream IDs to Surfaces
     81     private final SparseArray<Surface> mConfiguredOutputs = new SparseArray<Surface>();
     82 
     83     private final String mCameraId;
     84     private final CameraCharacteristics mCharacteristics;
     85     private final int mTotalPartialCount;
     86 
     87     /**
     88      * A list tracking request and its expected last frame.
     89      * Updated when calling ICameraDeviceUser methods.
     90      */
     91     private final List<SimpleEntry</*frameNumber*/Long, /*requestId*/Integer>>
     92             mFrameNumberRequestPairs = new ArrayList<SimpleEntry<Long, Integer>>();
     93 
     94     /**
     95      * An object tracking received frame numbers.
     96      * Updated when receiving callbacks from ICameraDeviceCallbacks.
     97      */
     98     private final FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker();
     99 
    100     private CameraCaptureSessionImpl mCurrentSession;
    101     private int mNextSessionId = 0;
    102 
    103     // Runnables for all state transitions, except error, which needs the
    104     // error code argument
    105 
    106     private final Runnable mCallOnOpened = new Runnable() {
    107         @Override
    108         public void run() {
    109             StateCallbackKK sessionCallback = null;
    110             synchronized(mInterfaceLock) {
    111                 if (mRemoteDevice == null) return; // Camera already closed
    112 
    113                 sessionCallback = mSessionStateCallback;
    114             }
    115             if (sessionCallback != null) {
    116                 sessionCallback.onOpened(CameraDeviceImpl.this);
    117             }
    118             mDeviceCallback.onOpened(CameraDeviceImpl.this);
    119         }
    120     };
    121 
    122     private final Runnable mCallOnUnconfigured = new Runnable() {
    123         @Override
    124         public void run() {
    125             StateCallbackKK sessionCallback = null;
    126             synchronized(mInterfaceLock) {
    127                 if (mRemoteDevice == null) return; // Camera already closed
    128 
    129                 sessionCallback = mSessionStateCallback;
    130             }
    131             if (sessionCallback != null) {
    132                 sessionCallback.onUnconfigured(CameraDeviceImpl.this);
    133             }
    134         }
    135     };
    136 
    137     private final Runnable mCallOnActive = new Runnable() {
    138         @Override
    139         public void run() {
    140             StateCallbackKK sessionCallback = null;
    141             synchronized(mInterfaceLock) {
    142                 if (mRemoteDevice == null) return; // Camera already closed
    143 
    144                 sessionCallback = mSessionStateCallback;
    145             }
    146             if (sessionCallback != null) {
    147                 sessionCallback.onActive(CameraDeviceImpl.this);
    148             }
    149         }
    150     };
    151 
    152     private final Runnable mCallOnBusy = new Runnable() {
    153         @Override
    154         public void run() {
    155             StateCallbackKK sessionCallback = null;
    156             synchronized(mInterfaceLock) {
    157                 if (mRemoteDevice == null) return; // Camera already closed
    158 
    159                 sessionCallback = mSessionStateCallback;
    160             }
    161             if (sessionCallback != null) {
    162                 sessionCallback.onBusy(CameraDeviceImpl.this);
    163             }
    164         }
    165     };
    166 
    167     private final Runnable mCallOnClosed = new Runnable() {
    168         private boolean mClosedOnce = false;
    169 
    170         @Override
    171         public void run() {
    172             if (mClosedOnce) {
    173                 throw new AssertionError("Don't post #onClosed more than once");
    174             }
    175             StateCallbackKK sessionCallback = null;
    176             synchronized(mInterfaceLock) {
    177                 sessionCallback = mSessionStateCallback;
    178             }
    179             if (sessionCallback != null) {
    180                 sessionCallback.onClosed(CameraDeviceImpl.this);
    181             }
    182             mDeviceCallback.onClosed(CameraDeviceImpl.this);
    183             mClosedOnce = true;
    184         }
    185     };
    186 
    187     private final Runnable mCallOnIdle = new Runnable() {
    188         @Override
    189         public void run() {
    190             StateCallbackKK sessionCallback = null;
    191             synchronized(mInterfaceLock) {
    192                 if (mRemoteDevice == null) return; // Camera already closed
    193 
    194                 sessionCallback = mSessionStateCallback;
    195             }
    196             if (sessionCallback != null) {
    197                 sessionCallback.onIdle(CameraDeviceImpl.this);
    198             }
    199         }
    200     };
    201 
    202     private final Runnable mCallOnDisconnected = new Runnable() {
    203         @Override
    204         public void run() {
    205             StateCallbackKK sessionCallback = null;
    206             synchronized(mInterfaceLock) {
    207                 if (mRemoteDevice == null) return; // Camera already closed
    208 
    209                 sessionCallback = mSessionStateCallback;
    210             }
    211             if (sessionCallback != null) {
    212                 sessionCallback.onDisconnected(CameraDeviceImpl.this);
    213             }
    214             mDeviceCallback.onDisconnected(CameraDeviceImpl.this);
    215         }
    216     };
    217 
    218     public CameraDeviceImpl(String cameraId, StateCallback callback, Handler handler,
    219                         CameraCharacteristics characteristics) {
    220         if (cameraId == null || callback == null || handler == null || characteristics == null) {
    221             throw new IllegalArgumentException("Null argument given");
    222         }
    223         mCameraId = cameraId;
    224         mDeviceCallback = callback;
    225         mDeviceHandler = handler;
    226         mCharacteristics = characteristics;
    227 
    228         final int MAX_TAG_LEN = 23;
    229         String tag = String.format("CameraDevice-JV-%s", mCameraId);
    230         if (tag.length() > MAX_TAG_LEN) {
    231             tag = tag.substring(0, MAX_TAG_LEN);
    232         }
    233         TAG = tag;
    234         DEBUG = Log.isLoggable(TAG, Log.DEBUG);
    235 
    236         Integer partialCount =
    237                 mCharacteristics.get(CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT);
    238         if (partialCount == null) {
    239             // 1 means partial result is not supported.
    240             mTotalPartialCount = 1;
    241         } else {
    242             mTotalPartialCount = partialCount;
    243         }
    244     }
    245 
    246     public CameraDeviceCallbacks getCallbacks() {
    247         return mCallbacks;
    248     }
    249 
    250     public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
    251         synchronized(mInterfaceLock) {
    252             // TODO: Move from decorator to direct binder-mediated exceptions
    253             // If setRemoteFailure already called, do nothing
    254             if (mInError) return;
    255 
    256             mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice);
    257 
    258             mDeviceHandler.post(mCallOnOpened);
    259             mDeviceHandler.post(mCallOnUnconfigured);
    260         }
    261     }
    262 
    263     /**
    264      * Call to indicate failed connection to a remote camera device.
    265      *
    266      * <p>This places the camera device in the error state and informs the callback.
    267      * Use in place of setRemoteDevice() when startup fails.</p>
    268      */
    269     public void setRemoteFailure(final CameraRuntimeException failure) {
    270         int failureCode = StateCallback.ERROR_CAMERA_DEVICE;
    271         boolean failureIsError = true;
    272 
    273         switch (failure.getReason()) {
    274             case CameraAccessException.CAMERA_IN_USE:
    275                 failureCode = StateCallback.ERROR_CAMERA_IN_USE;
    276                 break;
    277             case CameraAccessException.MAX_CAMERAS_IN_USE:
    278                 failureCode = StateCallback.ERROR_MAX_CAMERAS_IN_USE;
    279                 break;
    280             case CameraAccessException.CAMERA_DISABLED:
    281                 failureCode = StateCallback.ERROR_CAMERA_DISABLED;
    282                 break;
    283             case CameraAccessException.CAMERA_DISCONNECTED:
    284                 failureIsError = false;
    285                 break;
    286             case CameraAccessException.CAMERA_ERROR:
    287                 failureCode = StateCallback.ERROR_CAMERA_DEVICE;
    288                 break;
    289             default:
    290                 Log.wtf(TAG, "Unknown failure in opening camera device: " + failure.getReason());
    291                 break;
    292         }
    293         final int code = failureCode;
    294         final boolean isError = failureIsError;
    295         synchronized(mInterfaceLock) {
    296             mInError = true;
    297             mDeviceHandler.post(new Runnable() {
    298                 @Override
    299                 public void run() {
    300                     if (isError) {
    301                         mDeviceCallback.onError(CameraDeviceImpl.this, code);
    302                     } else {
    303                         mDeviceCallback.onDisconnected(CameraDeviceImpl.this);
    304                     }
    305                 }
    306             });
    307         }
    308     }
    309 
    310     @Override
    311     public String getId() {
    312         return mCameraId;
    313     }
    314 
    315     public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
    316         // Leave this here for backwards compatibility with older code using this directly
    317         configureOutputsChecked(outputs);
    318     }
    319 
    320     /**
    321      * Attempt to configure the outputs; the device goes to idle and then configures the
    322      * new outputs if possible.
    323      *
    324      * <p>The configuration may gracefully fail, if there are too many outputs, if the formats
    325      * are not supported, or if the sizes for that format is not supported. In this case this
    326      * function will return {@code false} and the unconfigured callback will be fired.</p>
    327      *
    328      * <p>If the configuration succeeds (with 1 or more outputs), then the idle callback is fired.
    329      * Unconfiguring the device always fires the idle callback.</p>
    330      *
    331      * @param outputs a list of one or more surfaces, or {@code null} to unconfigure
    332      * @return whether or not the configuration was successful
    333      *
    334      * @throws CameraAccessException if there were any unexpected problems during configuration
    335      */
    336     public boolean configureOutputsChecked(List<Surface> outputs) throws CameraAccessException {
    337         // Treat a null input the same an empty list
    338         if (outputs == null) {
    339             outputs = new ArrayList<Surface>();
    340         }
    341         boolean success = false;
    342 
    343         synchronized(mInterfaceLock) {
    344             checkIfCameraClosedOrInError();
    345 
    346             HashSet<Surface> addSet = new HashSet<Surface>(outputs);    // Streams to create
    347             List<Integer> deleteList = new ArrayList<Integer>();        // Streams to delete
    348 
    349             // Determine which streams need to be created, which to be deleted
    350             for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
    351                 int streamId = mConfiguredOutputs.keyAt(i);
    352                 Surface s = mConfiguredOutputs.valueAt(i);
    353 
    354                 if (!outputs.contains(s)) {
    355                     deleteList.add(streamId);
    356                 } else {
    357                     addSet.remove(s);  // Don't create a stream previously created
    358                 }
    359             }
    360 
    361             mDeviceHandler.post(mCallOnBusy);
    362             stopRepeating();
    363 
    364             try {
    365                 waitUntilIdle();
    366 
    367                 mRemoteDevice.beginConfigure();
    368                 // Delete all streams first (to free up HW resources)
    369                 for (Integer streamId : deleteList) {
    370                     mRemoteDevice.deleteStream(streamId);
    371                     mConfiguredOutputs.delete(streamId);
    372                 }
    373 
    374                 // Add all new streams
    375                 for (Surface s : addSet) {
    376                     // TODO: remove width,height,format since we are ignoring
    377                     // it.
    378                     int streamId = mRemoteDevice.createStream(0, 0, 0, s);
    379                     mConfiguredOutputs.put(streamId, s);
    380                 }
    381 
    382                 try {
    383                     mRemoteDevice.endConfigure();
    384                 }
    385                 catch (IllegalArgumentException e) {
    386                     // OK. camera service can reject stream config if it's not supported by HAL
    387                     // This is only the result of a programmer misusing the camera2 api.
    388                     Log.w(TAG, "Stream configuration failed");
    389                     return false;
    390                 }
    391 
    392                 success = true;
    393             } catch (CameraRuntimeException e) {
    394                 if (e.getReason() == CAMERA_IN_USE) {
    395                     throw new IllegalStateException("The camera is currently busy." +
    396                             " You must wait until the previous operation completes.");
    397                 }
    398 
    399                 throw e.asChecked();
    400             } catch (RemoteException e) {
    401                 // impossible
    402                 return false;
    403             } finally {
    404                 if (success && outputs.size() > 0) {
    405                     mDeviceHandler.post(mCallOnIdle);
    406                 } else {
    407                     // Always return to the 'unconfigured' state if we didn't hit a fatal error
    408                     mDeviceHandler.post(mCallOnUnconfigured);
    409                 }
    410             }
    411         }
    412 
    413         return success;
    414     }
    415 
    416     @Override
    417     public void createCaptureSession(List<Surface> outputs,
    418             CameraCaptureSession.StateCallback callback, Handler handler)
    419             throws CameraAccessException {
    420         synchronized(mInterfaceLock) {
    421             if (DEBUG) {
    422                 Log.d(TAG, "createCaptureSession");
    423             }
    424 
    425             checkIfCameraClosedOrInError();
    426 
    427             // Notify current session that it's going away, before starting camera operations
    428             // After this call completes, the session is not allowed to call into CameraDeviceImpl
    429             if (mCurrentSession != null) {
    430                 mCurrentSession.replaceSessionClose();
    431             }
    432 
    433             // TODO: dont block for this
    434             boolean configureSuccess = true;
    435             CameraAccessException pendingException = null;
    436             try {
    437                 configureSuccess = configureOutputsChecked(outputs); // and then block until IDLE
    438             } catch (CameraAccessException e) {
    439                 configureSuccess = false;
    440                 pendingException = e;
    441                 if (DEBUG) {
    442                     Log.v(TAG, "createCaptureSession - failed with exception ", e);
    443                 }
    444             }
    445 
    446             // Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise.
    447             CameraCaptureSessionImpl newSession =
    448                     new CameraCaptureSessionImpl(mNextSessionId++,
    449                             outputs, callback, handler, this, mDeviceHandler,
    450                             configureSuccess);
    451 
    452             // TODO: wait until current session closes, then create the new session
    453             mCurrentSession = newSession;
    454 
    455             if (pendingException != null) {
    456                 throw pendingException;
    457             }
    458 
    459             mSessionStateCallback = mCurrentSession.getDeviceStateCallback();
    460         }
    461     }
    462 
    463     /**
    464      * For use by backwards-compatibility code only.
    465      */
    466     public void setSessionListener(StateCallbackKK sessionCallback) {
    467         synchronized(mInterfaceLock) {
    468             mSessionStateCallback = sessionCallback;
    469         }
    470     }
    471 
    472     @Override
    473     public CaptureRequest.Builder createCaptureRequest(int templateType)
    474             throws CameraAccessException {
    475         synchronized(mInterfaceLock) {
    476             checkIfCameraClosedOrInError();
    477 
    478             CameraMetadataNative templatedRequest = new CameraMetadataNative();
    479 
    480             try {
    481                 mRemoteDevice.createDefaultRequest(templateType, /*out*/templatedRequest);
    482             } catch (CameraRuntimeException e) {
    483                 throw e.asChecked();
    484             } catch (RemoteException e) {
    485                 // impossible
    486                 return null;
    487             }
    488 
    489             CaptureRequest.Builder builder =
    490                     new CaptureRequest.Builder(templatedRequest);
    491 
    492             return builder;
    493         }
    494     }
    495 
    496     public int capture(CaptureRequest request, CaptureCallback callback, Handler handler)
    497             throws CameraAccessException {
    498         if (DEBUG) {
    499             Log.d(TAG, "calling capture");
    500         }
    501         List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
    502         requestList.add(request);
    503         return submitCaptureRequest(requestList, callback, handler, /*streaming*/false);
    504     }
    505 
    506     public int captureBurst(List<CaptureRequest> requests, CaptureCallback callback,
    507             Handler handler) throws CameraAccessException {
    508         if (requests == null || requests.isEmpty()) {
    509             throw new IllegalArgumentException("At least one request must be given");
    510         }
    511         return submitCaptureRequest(requests, callback, handler, /*streaming*/false);
    512     }
    513 
    514     /**
    515      * This method checks lastFrameNumber returned from ICameraDeviceUser methods for
    516      * starting and stopping repeating request and flushing.
    517      *
    518      * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never
    519      * sent to HAL. Then onCaptureSequenceAborted is immediately triggered.
    520      * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber pair
    521      * is added to the list mFrameNumberRequestPairs.</p>
    522      *
    523      * @param requestId the request ID of the current repeating request.
    524      *
    525      * @param lastFrameNumber last frame number returned from binder.
    526      */
    527     private void checkEarlyTriggerSequenceComplete(
    528             final int requestId, final long lastFrameNumber) {
    529         // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request
    530         // was never sent to HAL. Should trigger onCaptureSequenceAborted immediately.
    531         if (lastFrameNumber == CaptureCallback.NO_FRAMES_CAPTURED) {
    532             final CaptureCallbackHolder holder;
    533             int index = mCaptureCallbackMap.indexOfKey(requestId);
    534             holder = (index >= 0) ? mCaptureCallbackMap.valueAt(index) : null;
    535             if (holder != null) {
    536                 mCaptureCallbackMap.removeAt(index);
    537                 if (DEBUG) {
    538                     Log.v(TAG, String.format(
    539                             "remove holder for requestId %d, "
    540                             + "because lastFrame is %d.",
    541                             requestId, lastFrameNumber));
    542                 }
    543             }
    544 
    545             if (holder != null) {
    546                 if (DEBUG) {
    547                     Log.v(TAG, "immediately trigger onCaptureSequenceAborted because"
    548                             + " request did not reach HAL");
    549                 }
    550 
    551                 Runnable resultDispatch = new Runnable() {
    552                     @Override
    553                     public void run() {
    554                         if (!CameraDeviceImpl.this.isClosed()) {
    555                             if (DEBUG) {
    556                                 Log.d(TAG, String.format(
    557                                         "early trigger sequence complete for request %d",
    558                                         requestId));
    559                             }
    560                             if (lastFrameNumber < Integer.MIN_VALUE
    561                                     || lastFrameNumber > Integer.MAX_VALUE) {
    562                                 throw new AssertionError(lastFrameNumber + " cannot be cast to int");
    563                             }
    564                             holder.getCallback().onCaptureSequenceAborted(
    565                                     CameraDeviceImpl.this,
    566                                     requestId);
    567                         }
    568                     }
    569                 };
    570                 holder.getHandler().post(resultDispatch);
    571             } else {
    572                 Log.w(TAG, String.format(
    573                         "did not register callback to request %d",
    574                         requestId));
    575             }
    576         } else {
    577             mFrameNumberRequestPairs.add(
    578                     new SimpleEntry<Long, Integer>(lastFrameNumber,
    579                             requestId));
    580             // It is possible that the last frame has already arrived, so we need to check
    581             // for sequence completion right away
    582             checkAndFireSequenceComplete();
    583         }
    584     }
    585 
    586     private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback,
    587             Handler handler, boolean repeating) throws CameraAccessException {
    588 
    589         // Need a valid handler, or current thread needs to have a looper, if
    590         // callback is valid
    591         handler = checkHandler(handler, callback);
    592 
    593         // Make sure that there all requests have at least 1 surface; all surfaces are non-null
    594         for (CaptureRequest request : requestList) {
    595             if (request.getTargets().isEmpty()) {
    596                 throw new IllegalArgumentException(
    597                         "Each request must have at least one Surface target");
    598             }
    599 
    600             for (Surface surface : request.getTargets()) {
    601                 if (surface == null) {
    602                     throw new IllegalArgumentException("Null Surface targets are not allowed");
    603                 }
    604             }
    605         }
    606 
    607         synchronized(mInterfaceLock) {
    608             checkIfCameraClosedOrInError();
    609             int requestId;
    610 
    611             if (repeating) {
    612                 stopRepeating();
    613             }
    614 
    615             LongParcelable lastFrameNumberRef = new LongParcelable();
    616             try {
    617                 requestId = mRemoteDevice.submitRequestList(requestList, repeating,
    618                         /*out*/lastFrameNumberRef);
    619                 if (DEBUG) {
    620                     Log.v(TAG, "last frame number " + lastFrameNumberRef.getNumber());
    621                 }
    622             } catch (CameraRuntimeException e) {
    623                 throw e.asChecked();
    624             } catch (RemoteException e) {
    625                 // impossible
    626                 return -1;
    627             }
    628 
    629             if (callback != null) {
    630                 mCaptureCallbackMap.put(requestId, new CaptureCallbackHolder(callback,
    631                         requestList, handler, repeating));
    632             } else {
    633                 if (DEBUG) {
    634                     Log.d(TAG, "Listen for request " + requestId + " is null");
    635                 }
    636             }
    637 
    638             long lastFrameNumber = lastFrameNumberRef.getNumber();
    639 
    640             if (repeating) {
    641                 if (mRepeatingRequestId != REQUEST_ID_NONE) {
    642                     checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
    643                 }
    644                 mRepeatingRequestId = requestId;
    645             } else {
    646                 mFrameNumberRequestPairs.add(
    647                         new SimpleEntry<Long, Integer>(lastFrameNumber, requestId));
    648             }
    649 
    650             if (mIdle) {
    651                 mDeviceHandler.post(mCallOnActive);
    652             }
    653             mIdle = false;
    654 
    655             return requestId;
    656         }
    657     }
    658 
    659     public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback,
    660             Handler handler) throws CameraAccessException {
    661         List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
    662         requestList.add(request);
    663         return submitCaptureRequest(requestList, callback, handler, /*streaming*/true);
    664     }
    665 
    666     public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback callback,
    667             Handler handler) throws CameraAccessException {
    668         if (requests == null || requests.isEmpty()) {
    669             throw new IllegalArgumentException("At least one request must be given");
    670         }
    671         return submitCaptureRequest(requests, callback, handler, /*streaming*/true);
    672     }
    673 
    674     public void stopRepeating() throws CameraAccessException {
    675 
    676         synchronized(mInterfaceLock) {
    677             checkIfCameraClosedOrInError();
    678             if (mRepeatingRequestId != REQUEST_ID_NONE) {
    679 
    680                 int requestId = mRepeatingRequestId;
    681                 mRepeatingRequestId = REQUEST_ID_NONE;
    682 
    683                 // Queue for deletion after in-flight requests finish
    684                 if (mCaptureCallbackMap.get(requestId) != null) {
    685                     mRepeatingRequestIdDeletedList.add(requestId);
    686                 }
    687 
    688                 try {
    689                     LongParcelable lastFrameNumberRef = new LongParcelable();
    690                     mRemoteDevice.cancelRequest(requestId, /*out*/lastFrameNumberRef);
    691                     long lastFrameNumber = lastFrameNumberRef.getNumber();
    692 
    693                     checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber);
    694 
    695                 } catch (CameraRuntimeException e) {
    696                     throw e.asChecked();
    697                 } catch (RemoteException e) {
    698                     // impossible
    699                     return;
    700                 }
    701             }
    702         }
    703     }
    704 
    705     private void waitUntilIdle() throws CameraAccessException {
    706 
    707         synchronized(mInterfaceLock) {
    708             checkIfCameraClosedOrInError();
    709 
    710             if (mRepeatingRequestId != REQUEST_ID_NONE) {
    711                 throw new IllegalStateException("Active repeating request ongoing");
    712             }
    713             try {
    714                 mRemoteDevice.waitUntilIdle();
    715             } catch (CameraRuntimeException e) {
    716                 throw e.asChecked();
    717             } catch (RemoteException e) {
    718                 // impossible
    719                 return;
    720             }
    721         }
    722     }
    723 
    724     public void flush() throws CameraAccessException {
    725         synchronized(mInterfaceLock) {
    726             checkIfCameraClosedOrInError();
    727 
    728             mDeviceHandler.post(mCallOnBusy);
    729 
    730             // If already idle, just do a busy->idle transition immediately, don't actually
    731             // flush.
    732             if (mIdle) {
    733                 mDeviceHandler.post(mCallOnIdle);
    734                 return;
    735             }
    736             try {
    737                 LongParcelable lastFrameNumberRef = new LongParcelable();
    738                 mRemoteDevice.flush(/*out*/lastFrameNumberRef);
    739                 if (mRepeatingRequestId != REQUEST_ID_NONE) {
    740                     long lastFrameNumber = lastFrameNumberRef.getNumber();
    741                     checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
    742                     mRepeatingRequestId = REQUEST_ID_NONE;
    743                 }
    744             } catch (CameraRuntimeException e) {
    745                 throw e.asChecked();
    746             } catch (RemoteException e) {
    747                 // impossible
    748                 return;
    749             }
    750         }
    751     }
    752 
    753     @Override
    754     public void close() {
    755         synchronized (mInterfaceLock) {
    756             try {
    757                 if (mRemoteDevice != null) {
    758                     mRemoteDevice.disconnect();
    759                 }
    760             } catch (CameraRuntimeException e) {
    761                 Log.e(TAG, "Exception while closing: ", e.asChecked());
    762             } catch (RemoteException e) {
    763                 // impossible
    764             }
    765 
    766             // Only want to fire the onClosed callback once;
    767             // either a normal close where the remote device is valid
    768             // or a close after a startup error (no remote device but in error state)
    769             if (mRemoteDevice != null || mInError) {
    770                 mDeviceHandler.post(mCallOnClosed);
    771             }
    772 
    773             mRemoteDevice = null;
    774             mInError = false;
    775         }
    776     }
    777 
    778     @Override
    779     protected void finalize() throws Throwable {
    780         try {
    781             close();
    782         }
    783         finally {
    784             super.finalize();
    785         }
    786     }
    787 
    788     /**
    789      * <p>A callback for tracking the progress of a {@link CaptureRequest}
    790      * submitted to the camera device.</p>
    791      *
    792      */
    793     public static abstract class CaptureCallback {
    794 
    795         /**
    796          * This constant is used to indicate that no images were captured for
    797          * the request.
    798          *
    799          * @hide
    800          */
    801         public static final int NO_FRAMES_CAPTURED = -1;
    802 
    803         /**
    804          * This method is called when the camera device has started capturing
    805          * the output image for the request, at the beginning of image exposure.
    806          *
    807          * @see android.media.MediaActionSound
    808          */
    809         public void onCaptureStarted(CameraDevice camera,
    810                 CaptureRequest request, long timestamp, long frameNumber) {
    811             // default empty implementation
    812         }
    813 
    814         /**
    815          * This method is called when some results from an image capture are
    816          * available.
    817          *
    818          * @hide
    819          */
    820         public void onCapturePartial(CameraDevice camera,
    821                 CaptureRequest request, CaptureResult result) {
    822             // default empty implementation
    823         }
    824 
    825         /**
    826          * This method is called when an image capture makes partial forward progress; some
    827          * (but not all) results from an image capture are available.
    828          *
    829          */
    830         public void onCaptureProgressed(CameraDevice camera,
    831                 CaptureRequest request, CaptureResult partialResult) {
    832             // default empty implementation
    833         }
    834 
    835         /**
    836          * This method is called when an image capture has fully completed and all the
    837          * result metadata is available.
    838          */
    839         public void onCaptureCompleted(CameraDevice camera,
    840                 CaptureRequest request, TotalCaptureResult result) {
    841             // default empty implementation
    842         }
    843 
    844         /**
    845          * This method is called instead of {@link #onCaptureCompleted} when the
    846          * camera device failed to produce a {@link CaptureResult} for the
    847          * request.
    848          */
    849         public void onCaptureFailed(CameraDevice camera,
    850                 CaptureRequest request, CaptureFailure failure) {
    851             // default empty implementation
    852         }
    853 
    854         /**
    855          * This method is called independently of the others in CaptureCallback,
    856          * when a capture sequence finishes and all {@link CaptureResult}
    857          * or {@link CaptureFailure} for it have been returned via this callback.
    858          */
    859         public void onCaptureSequenceCompleted(CameraDevice camera,
    860                 int sequenceId, long frameNumber) {
    861             // default empty implementation
    862         }
    863 
    864         /**
    865          * This method is called independently of the others in CaptureCallback,
    866          * when a capture sequence aborts before any {@link CaptureResult}
    867          * or {@link CaptureFailure} for it have been returned via this callback.
    868          */
    869         public void onCaptureSequenceAborted(CameraDevice camera,
    870                 int sequenceId) {
    871             // default empty implementation
    872         }
    873     }
    874 
    875     /**
    876      * A callback for notifications about the state of a camera device, adding in the callbacks that
    877      * were part of the earlier KK API design, but now only used internally.
    878      */
    879     public static abstract class StateCallbackKK extends StateCallback {
    880         /**
    881          * The method called when a camera device has no outputs configured.
    882          *
    883          */
    884         public void onUnconfigured(CameraDevice camera) {
    885             // Default empty implementation
    886         }
    887 
    888         /**
    889          * The method called when a camera device begins processing
    890          * {@link CaptureRequest capture requests}.
    891          *
    892          */
    893         public void onActive(CameraDevice camera) {
    894             // Default empty implementation
    895         }
    896 
    897         /**
    898          * The method called when a camera device is busy.
    899          *
    900          */
    901         public void onBusy(CameraDevice camera) {
    902             // Default empty implementation
    903         }
    904 
    905         /**
    906          * The method called when a camera device has finished processing all
    907          * submitted capture requests and has reached an idle state.
    908          *
    909          */
    910         public void onIdle(CameraDevice camera) {
    911             // Default empty implementation
    912         }
    913     }
    914 
    915     static class CaptureCallbackHolder {
    916 
    917         private final boolean mRepeating;
    918         private final CaptureCallback mCallback;
    919         private final List<CaptureRequest> mRequestList;
    920         private final Handler mHandler;
    921 
    922         CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList,
    923                 Handler handler, boolean repeating) {
    924             if (callback == null || handler == null) {
    925                 throw new UnsupportedOperationException(
    926                     "Must have a valid handler and a valid callback");
    927             }
    928             mRepeating = repeating;
    929             mHandler = handler;
    930             mRequestList = new ArrayList<CaptureRequest>(requestList);
    931             mCallback = callback;
    932         }
    933 
    934         public boolean isRepeating() {
    935             return mRepeating;
    936         }
    937 
    938         public CaptureCallback getCallback() {
    939             return mCallback;
    940         }
    941 
    942         public CaptureRequest getRequest(int subsequenceId) {
    943             if (subsequenceId >= mRequestList.size()) {
    944                 throw new IllegalArgumentException(
    945                         String.format(
    946                                 "Requested subsequenceId %d is larger than request list size %d.",
    947                                 subsequenceId, mRequestList.size()));
    948             } else {
    949                 if (subsequenceId < 0) {
    950                     throw new IllegalArgumentException(String.format(
    951                             "Requested subsequenceId %d is negative", subsequenceId));
    952                 } else {
    953                     return mRequestList.get(subsequenceId);
    954                 }
    955             }
    956         }
    957 
    958         public CaptureRequest getRequest() {
    959             return getRequest(0);
    960         }
    961 
    962         public Handler getHandler() {
    963             return mHandler;
    964         }
    965 
    966     }
    967 
    968     /**
    969      * This class tracks the last frame number for submitted requests.
    970      */
    971     public class FrameNumberTracker {
    972 
    973         private long mCompletedFrameNumber = -1;
    974         private final TreeSet<Long> mFutureErrorSet = new TreeSet<Long>();
    975         /** Map frame numbers to list of partial results */
    976         private final HashMap<Long, List<CaptureResult>> mPartialResults = new HashMap<>();
    977 
    978         private void update() {
    979             Iterator<Long> iter = mFutureErrorSet.iterator();
    980             while (iter.hasNext()) {
    981                 long errorFrameNumber = iter.next();
    982                 if (errorFrameNumber == mCompletedFrameNumber + 1) {
    983                     mCompletedFrameNumber++;
    984                     iter.remove();
    985                 } else {
    986                     break;
    987                 }
    988             }
    989         }
    990 
    991         /**
    992          * This function is called every time when a result or an error is received.
    993          * @param frameNumber the frame number corresponding to the result or error
    994          * @param isError true if it is an error, false if it is not an error
    995          */
    996         public void updateTracker(long frameNumber, boolean isError) {
    997             if (isError) {
    998                 mFutureErrorSet.add(frameNumber);
    999             } else {
   1000                 /**
   1001                  * HAL cannot send an OnResultReceived for frame N unless it knows for
   1002                  * sure that all frames prior to N have either errored out or completed.
   1003                  * So if the current frame is not an error, then all previous frames
   1004                  * should have arrived. The following line checks whether this holds.
   1005                  */
   1006                 if (frameNumber != mCompletedFrameNumber + 1) {
   1007                     Log.e(TAG, String.format(
   1008                             "result frame number %d comes out of order, should be %d + 1",
   1009                             frameNumber, mCompletedFrameNumber));
   1010                     // Continue on to set the completed frame number to this frame anyway,
   1011                     // to be robust to lower-level errors and allow for clean shutdowns.
   1012                 }
   1013                 mCompletedFrameNumber = frameNumber;
   1014             }
   1015             update();
   1016         }
   1017 
   1018         /**
   1019          * This function is called every time a result has been completed.
   1020          *
   1021          * <p>It keeps a track of all the partial results already created for a particular
   1022          * frame number.</p>
   1023          *
   1024          * @param frameNumber the frame number corresponding to the result
   1025          * @param result the total or partial result
   1026          * @param partial {@true} if the result is partial, {@code false} if total
   1027          */
   1028         public void updateTracker(long frameNumber, CaptureResult result, boolean partial) {
   1029 
   1030             if (!partial) {
   1031                 // Update the total result's frame status as being successful
   1032                 updateTracker(frameNumber, /*isError*/false);
   1033                 // Don't keep a list of total results, we don't need to track them
   1034                 return;
   1035             }
   1036 
   1037             if (result == null) {
   1038                 // Do not record blank results; this also means there will be no total result
   1039                 // so it doesn't matter that the partials were not recorded
   1040                 return;
   1041             }
   1042 
   1043             // Partial results must be aggregated in-order for that frame number
   1044             List<CaptureResult> partials = mPartialResults.get(frameNumber);
   1045             if (partials == null) {
   1046                 partials = new ArrayList<>();
   1047                 mPartialResults.put(frameNumber, partials);
   1048             }
   1049 
   1050             partials.add(result);
   1051         }
   1052 
   1053         /**
   1054          * Attempt to pop off all of the partial results seen so far for the {@code frameNumber}.
   1055          *
   1056          * <p>Once popped-off, the partial results are forgotten (unless {@code updateTracker}
   1057          * is called again with new partials for that frame number).</p>
   1058          *
   1059          * @param frameNumber the frame number corresponding to the result
   1060          * @return a list of partial results for that frame with at least 1 element,
   1061          *         or {@code null} if there were no partials recorded for that frame
   1062          */
   1063         public List<CaptureResult> popPartialResults(long frameNumber) {
   1064             return mPartialResults.remove(frameNumber);
   1065         }
   1066 
   1067         public long getCompletedFrameNumber() {
   1068             return mCompletedFrameNumber;
   1069         }
   1070 
   1071     }
   1072 
   1073     private void checkAndFireSequenceComplete() {
   1074         long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
   1075         Iterator<SimpleEntry<Long, Integer> > iter = mFrameNumberRequestPairs.iterator();
   1076         while (iter.hasNext()) {
   1077             final SimpleEntry<Long, Integer> frameNumberRequestPair = iter.next();
   1078             if (frameNumberRequestPair.getKey() <= completedFrameNumber) {
   1079 
   1080                 // remove request from mCaptureCallbackMap
   1081                 final int requestId = frameNumberRequestPair.getValue();
   1082                 final CaptureCallbackHolder holder;
   1083                 synchronized(mInterfaceLock) {
   1084                     if (mRemoteDevice == null) {
   1085                         Log.w(TAG, "Camera closed while checking sequences");
   1086                         return;
   1087                     }
   1088 
   1089                     int index = mCaptureCallbackMap.indexOfKey(requestId);
   1090                     holder = (index >= 0) ? mCaptureCallbackMap.valueAt(index)
   1091                             : null;
   1092                     if (holder != null) {
   1093                         mCaptureCallbackMap.removeAt(index);
   1094                         if (DEBUG) {
   1095                             Log.v(TAG, String.format(
   1096                                     "remove holder for requestId %d, "
   1097                                     + "because lastFrame %d is <= %d",
   1098                                     requestId, frameNumberRequestPair.getKey(),
   1099                                     completedFrameNumber));
   1100                         }
   1101                     }
   1102                 }
   1103                 iter.remove();
   1104 
   1105                 // Call onCaptureSequenceCompleted
   1106                 if (holder != null) {
   1107                     Runnable resultDispatch = new Runnable() {
   1108                         @Override
   1109                         public void run() {
   1110                             if (!CameraDeviceImpl.this.isClosed()){
   1111                                 if (DEBUG) {
   1112                                     Log.d(TAG, String.format(
   1113                                             "fire sequence complete for request %d",
   1114                                             requestId));
   1115                                 }
   1116 
   1117                                 long lastFrameNumber = frameNumberRequestPair.getKey();
   1118                                 if (lastFrameNumber < Integer.MIN_VALUE
   1119                                         || lastFrameNumber > Integer.MAX_VALUE) {
   1120                                     throw new AssertionError(lastFrameNumber
   1121                                             + " cannot be cast to int");
   1122                                 }
   1123                                 holder.getCallback().onCaptureSequenceCompleted(
   1124                                     CameraDeviceImpl.this,
   1125                                     requestId,
   1126                                     lastFrameNumber);
   1127                             }
   1128                         }
   1129                     };
   1130                     holder.getHandler().post(resultDispatch);
   1131                 }
   1132 
   1133             }
   1134         }
   1135     }
   1136 
   1137     public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
   1138         //
   1139         // Constants below need to be kept up-to-date with
   1140         // frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h
   1141         //
   1142 
   1143         //
   1144         // Error codes for onCameraError
   1145         //
   1146 
   1147         /**
   1148          * Camera has been disconnected
   1149          */
   1150         public static final int ERROR_CAMERA_DISCONNECTED = 0;
   1151         /**
   1152          * Camera has encountered a device-level error
   1153          * Matches CameraDevice.StateCallback#ERROR_CAMERA_DEVICE
   1154          */
   1155         public static final int ERROR_CAMERA_DEVICE = 1;
   1156         /**
   1157          * Camera has encountered a service-level error
   1158          * Matches CameraDevice.StateCallback#ERROR_CAMERA_SERVICE
   1159          */
   1160         public static final int ERROR_CAMERA_SERVICE = 2;
   1161         /**
   1162          * Camera has encountered an error processing a single request.
   1163          */
   1164         public static final int ERROR_CAMERA_REQUEST = 3;
   1165         /**
   1166          * Camera has encountered an error producing metadata for a single capture
   1167          */
   1168         public static final int ERROR_CAMERA_RESULT = 4;
   1169         /**
   1170          * Camera has encountered an error producing an image buffer for a single capture
   1171          */
   1172         public static final int ERROR_CAMERA_BUFFER = 5;
   1173 
   1174         @Override
   1175         public IBinder asBinder() {
   1176             return this;
   1177         }
   1178 
   1179         @Override
   1180         public void onDeviceError(final int errorCode, CaptureResultExtras resultExtras) {
   1181             if (DEBUG) {
   1182                 Log.d(TAG, String.format(
   1183                         "Device error received, code %d, frame number %d, request ID %d, subseq ID %d",
   1184                         errorCode, resultExtras.getFrameNumber(), resultExtras.getRequestId(),
   1185                         resultExtras.getSubsequenceId()));
   1186             }
   1187 
   1188             synchronized(mInterfaceLock) {
   1189                 if (mRemoteDevice == null) {
   1190                     return; // Camera already closed
   1191                 }
   1192 
   1193                 switch (errorCode) {
   1194                     case ERROR_CAMERA_DISCONNECTED:
   1195                         CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected);
   1196                         break;
   1197                     default:
   1198                         Log.e(TAG, "Unknown error from camera device: " + errorCode);
   1199                         // no break
   1200                     case ERROR_CAMERA_DEVICE:
   1201                     case ERROR_CAMERA_SERVICE:
   1202                         mInError = true;
   1203                         Runnable r = new Runnable() {
   1204                             @Override
   1205                             public void run() {
   1206                                 if (!CameraDeviceImpl.this.isClosed()) {
   1207                                     mDeviceCallback.onError(CameraDeviceImpl.this, errorCode);
   1208                                 }
   1209                             }
   1210                         };
   1211                         CameraDeviceImpl.this.mDeviceHandler.post(r);
   1212                         break;
   1213                     case ERROR_CAMERA_REQUEST:
   1214                     case ERROR_CAMERA_RESULT:
   1215                     case ERROR_CAMERA_BUFFER:
   1216                         onCaptureErrorLocked(errorCode, resultExtras);
   1217                         break;
   1218                 }
   1219             }
   1220         }
   1221 
   1222         @Override
   1223         public void onDeviceIdle() {
   1224             if (DEBUG) {
   1225                 Log.d(TAG, "Camera now idle");
   1226             }
   1227             synchronized(mInterfaceLock) {
   1228                 if (mRemoteDevice == null) return; // Camera already closed
   1229 
   1230                 if (!CameraDeviceImpl.this.mIdle) {
   1231                     CameraDeviceImpl.this.mDeviceHandler.post(mCallOnIdle);
   1232                 }
   1233                 CameraDeviceImpl.this.mIdle = true;
   1234             }
   1235         }
   1236 
   1237         @Override
   1238         public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) {
   1239             int requestId = resultExtras.getRequestId();
   1240             final long frameNumber = resultExtras.getFrameNumber();
   1241 
   1242             if (DEBUG) {
   1243                 Log.d(TAG, "Capture started for id " + requestId + " frame number " + frameNumber);
   1244             }
   1245             final CaptureCallbackHolder holder;
   1246 
   1247             synchronized(mInterfaceLock) {
   1248                 if (mRemoteDevice == null) return; // Camera already closed
   1249 
   1250                 // Get the callback for this frame ID, if there is one
   1251                 holder = CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId);
   1252 
   1253                 if (holder == null) {
   1254                     return;
   1255                 }
   1256 
   1257                 if (isClosed()) return;
   1258 
   1259                 // Dispatch capture start notice
   1260                 holder.getHandler().post(
   1261                     new Runnable() {
   1262                         @Override
   1263                         public void run() {
   1264                             if (!CameraDeviceImpl.this.isClosed()) {
   1265                                 holder.getCallback().onCaptureStarted(
   1266                                     CameraDeviceImpl.this,
   1267                                     holder.getRequest(resultExtras.getSubsequenceId()),
   1268                                     timestamp, frameNumber);
   1269                             }
   1270                         }
   1271                     });
   1272 
   1273             }
   1274         }
   1275 
   1276         @Override
   1277         public void onResultReceived(CameraMetadataNative result,
   1278                 CaptureResultExtras resultExtras) throws RemoteException {
   1279 
   1280             int requestId = resultExtras.getRequestId();
   1281             long frameNumber = resultExtras.getFrameNumber();
   1282 
   1283             if (DEBUG) {
   1284                 Log.v(TAG, "Received result frame " + frameNumber + " for id "
   1285                         + requestId);
   1286             }
   1287 
   1288             synchronized(mInterfaceLock) {
   1289                 if (mRemoteDevice == null) return; // Camera already closed
   1290 
   1291                 // TODO: Handle CameraCharacteristics access from CaptureResult correctly.
   1292                 result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE,
   1293                         getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE));
   1294 
   1295                 final CaptureCallbackHolder holder =
   1296                         CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId);
   1297 
   1298                 boolean isPartialResult =
   1299                         (resultExtras.getPartialResultCount() < mTotalPartialCount);
   1300 
   1301                 // Check if we have a callback for this
   1302                 if (holder == null) {
   1303                     if (DEBUG) {
   1304                         Log.d(TAG,
   1305                                 "holder is null, early return at frame "
   1306                                         + frameNumber);
   1307                     }
   1308 
   1309                     mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult);
   1310 
   1311                     return;
   1312                 }
   1313 
   1314                 if (isClosed()) {
   1315                     if (DEBUG) {
   1316                         Log.d(TAG,
   1317                                 "camera is closed, early return at frame "
   1318                                         + frameNumber);
   1319                     }
   1320 
   1321                     mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult);
   1322                     return;
   1323                 }
   1324 
   1325                 final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
   1326 
   1327                 Runnable resultDispatch = null;
   1328 
   1329                 CaptureResult finalResult;
   1330 
   1331                 // Either send a partial result or the final capture completed result
   1332                 if (isPartialResult) {
   1333                     final CaptureResult resultAsCapture =
   1334                             new CaptureResult(result, request, resultExtras);
   1335 
   1336                     // Partial result
   1337                     resultDispatch = new Runnable() {
   1338                         @Override
   1339                         public void run() {
   1340                             if (!CameraDeviceImpl.this.isClosed()){
   1341                                 holder.getCallback().onCaptureProgressed(
   1342                                     CameraDeviceImpl.this,
   1343                                     request,
   1344                                     resultAsCapture);
   1345                             }
   1346                         }
   1347                     };
   1348 
   1349                     finalResult = resultAsCapture;
   1350                 } else {
   1351                     List<CaptureResult> partialResults =
   1352                             mFrameNumberTracker.popPartialResults(frameNumber);
   1353 
   1354                     final TotalCaptureResult resultAsCapture =
   1355                             new TotalCaptureResult(result, request, resultExtras, partialResults);
   1356 
   1357                     // Final capture result
   1358                     resultDispatch = new Runnable() {
   1359                         @Override
   1360                         public void run() {
   1361                             if (!CameraDeviceImpl.this.isClosed()){
   1362                                 holder.getCallback().onCaptureCompleted(
   1363                                     CameraDeviceImpl.this,
   1364                                     request,
   1365                                     resultAsCapture);
   1366                             }
   1367                         }
   1368                     };
   1369 
   1370                     finalResult = resultAsCapture;
   1371                 }
   1372 
   1373                 holder.getHandler().post(resultDispatch);
   1374 
   1375                 // Collect the partials for a total result; or mark the frame as totally completed
   1376                 mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult);
   1377 
   1378                 // Fire onCaptureSequenceCompleted
   1379                 if (!isPartialResult) {
   1380                     checkAndFireSequenceComplete();
   1381                 }
   1382             }
   1383         }
   1384 
   1385         /**
   1386          * Called by onDeviceError for handling single-capture failures.
   1387          */
   1388         private void onCaptureErrorLocked(int errorCode, CaptureResultExtras resultExtras) {
   1389 
   1390             final int requestId = resultExtras.getRequestId();
   1391             final int subsequenceId = resultExtras.getSubsequenceId();
   1392             final long frameNumber = resultExtras.getFrameNumber();
   1393             final CaptureCallbackHolder holder =
   1394                     CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId);
   1395 
   1396             final CaptureRequest request = holder.getRequest(subsequenceId);
   1397 
   1398             // No way to report buffer errors right now
   1399             if (errorCode == ERROR_CAMERA_BUFFER) {
   1400                 Log.e(TAG, String.format("Lost output buffer reported for frame %d", frameNumber));
   1401                 return;
   1402             }
   1403 
   1404             boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT);
   1405 
   1406             // This is only approximate - exact handling needs the camera service and HAL to
   1407             // disambiguate between request failures to due abort and due to real errors.
   1408             // For now, assume that if the session believes we're mid-abort, then the error
   1409             // is due to abort.
   1410             int reason = (mCurrentSession != null && mCurrentSession.isAborting()) ?
   1411                     CaptureFailure.REASON_FLUSHED :
   1412                     CaptureFailure.REASON_ERROR;
   1413 
   1414             final CaptureFailure failure = new CaptureFailure(
   1415                 request,
   1416                 reason,
   1417                 /*dropped*/ mayHaveBuffers,
   1418                 requestId,
   1419                 frameNumber);
   1420 
   1421             Runnable failureDispatch = new Runnable() {
   1422                 @Override
   1423                 public void run() {
   1424                     if (!CameraDeviceImpl.this.isClosed()){
   1425                         holder.getCallback().onCaptureFailed(
   1426                             CameraDeviceImpl.this,
   1427                             request,
   1428                             failure);
   1429                     }
   1430                 }
   1431             };
   1432             holder.getHandler().post(failureDispatch);
   1433 
   1434             // Fire onCaptureSequenceCompleted if appropriate
   1435             if (DEBUG) {
   1436                 Log.v(TAG, String.format("got error frame %d", frameNumber));
   1437             }
   1438             mFrameNumberTracker.updateTracker(frameNumber, /*error*/true);
   1439             checkAndFireSequenceComplete();
   1440         }
   1441 
   1442     } // public class CameraDeviceCallbacks
   1443 
   1444     /**
   1445      * Default handler management.
   1446      *
   1447      * <p>
   1448      * If handler is null, get the current thread's
   1449      * Looper to create a Handler with. If no looper exists, throw {@code IllegalArgumentException}.
   1450      * </p>
   1451      */
   1452     static Handler checkHandler(Handler handler) {
   1453         if (handler == null) {
   1454             Looper looper = Looper.myLooper();
   1455             if (looper == null) {
   1456                 throw new IllegalArgumentException(
   1457                     "No handler given, and current thread has no looper!");
   1458             }
   1459             handler = new Handler(looper);
   1460         }
   1461         return handler;
   1462     }
   1463 
   1464     /**
   1465      * Default handler management, conditional on there being a callback.
   1466      *
   1467      * <p>If the callback isn't null, check the handler, otherwise pass it through.</p>
   1468      */
   1469     static <T> Handler checkHandler(Handler handler, T callback) {
   1470         if (callback != null) {
   1471             return checkHandler(handler);
   1472         }
   1473         return handler;
   1474     }
   1475 
   1476     private void checkIfCameraClosedOrInError() throws CameraAccessException {
   1477         if (mInError) {
   1478             throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
   1479                     "The camera device has encountered a serious error");
   1480         }
   1481         if (mRemoteDevice == null) {
   1482             throw new IllegalStateException("CameraDevice was already closed");
   1483         }
   1484     }
   1485 
   1486     /** Whether the camera device has started to close (may not yet have finished) */
   1487     private boolean isClosed() {
   1488         return mClosing;
   1489     }
   1490 
   1491     private CameraCharacteristics getCharacteristics() {
   1492         return mCharacteristics;
   1493     }
   1494 }
   1495