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 
CameraDeviceImpl(String cameraId, StateCallback callback, Handler handler, CameraCharacteristics characteristics)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 
getCallbacks()246     public CameraDeviceCallbacks getCallbacks() {
247         return mCallbacks;
248     }
249 
setRemoteDevice(ICameraDeviceUser remoteDevice)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      */
setRemoteFailure(final CameraRuntimeException failure)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
getId()311     public String getId() {
312         return mCameraId;
313     }
314 
configureOutputs(List<Surface> outputs)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      */
configureOutputsChecked(List<Surface> outputs)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
createCaptureSession(List<Surface> outputs, CameraCaptureSession.StateCallback callback, Handler handler)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      */
setSessionListener(StateCallbackKK sessionCallback)466     public void setSessionListener(StateCallbackKK sessionCallback) {
467         synchronized(mInterfaceLock) {
468             mSessionStateCallback = sessionCallback;
469         }
470     }
471 
472     @Override
createCaptureRequest(int templateType)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 
capture(CaptureRequest request, CaptureCallback callback, Handler handler)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 
captureBurst(List<CaptureRequest> requests, CaptureCallback callback, Handler handler)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      */
checkEarlyTriggerSequenceComplete( final int requestId, final long lastFrameNumber)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 
submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback, Handler handler, boolean repeating)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 
setRepeatingRequest(CaptureRequest request, CaptureCallback callback, Handler handler)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 
setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback callback, Handler handler)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 
stopRepeating()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 
waitUntilIdle()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 
flush()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
close()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
finalize()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          */
onCaptureStarted(CameraDevice camera, CaptureRequest request, long timestamp, long frameNumber)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          */
onCapturePartial(CameraDevice camera, CaptureRequest request, CaptureResult result)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          */
onCaptureProgressed(CameraDevice camera, CaptureRequest request, CaptureResult partialResult)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          */
onCaptureCompleted(CameraDevice camera, CaptureRequest request, TotalCaptureResult result)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          */
onCaptureFailed(CameraDevice camera, CaptureRequest request, CaptureFailure failure)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          */
onCaptureSequenceCompleted(CameraDevice camera, int sequenceId, long frameNumber)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          */
onCaptureSequenceAborted(CameraDevice camera, int sequenceId)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          */
onUnconfigured(CameraDevice camera)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          */
onActive(CameraDevice camera)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          */
onBusy(CameraDevice camera)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          */
onIdle(CameraDevice camera)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 
CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList, Handler handler, boolean repeating)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 
isRepeating()934         public boolean isRepeating() {
935             return mRepeating;
936         }
937 
getCallback()938         public CaptureCallback getCallback() {
939             return mCallback;
940         }
941 
getRequest(int subsequenceId)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 
getRequest()958         public CaptureRequest getRequest() {
959             return getRequest(0);
960         }
961 
getHandler()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 
update()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          */
updateTracker(long frameNumber, boolean isError)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          */
updateTracker(long frameNumber, CaptureResult result, boolean partial)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          */
popPartialResults(long frameNumber)1063         public List<CaptureResult> popPartialResults(long frameNumber) {
1064             return mPartialResults.remove(frameNumber);
1065         }
1066 
getCompletedFrameNumber()1067         public long getCompletedFrameNumber() {
1068             return mCompletedFrameNumber;
1069         }
1070 
1071     }
1072 
checkAndFireSequenceComplete()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
asBinder()1175         public IBinder asBinder() {
1176             return this;
1177         }
1178 
1179         @Override
onDeviceError(final int errorCode, CaptureResultExtras resultExtras)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
onDeviceIdle()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
onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp)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
onResultReceived(CameraMetadataNative result, CaptureResultExtras resultExtras)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          */
onCaptureErrorLocked(int errorCode, CaptureResultExtras resultExtras)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      */
checkHandler(Handler handler)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      */
checkHandler(Handler handler, T callback)1469     static <T> Handler checkHandler(Handler handler, T callback) {
1470         if (callback != null) {
1471             return checkHandler(handler);
1472         }
1473         return handler;
1474     }
1475 
checkIfCameraClosedOrInError()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) */
isClosed()1487     private boolean isClosed() {
1488         return mClosing;
1489     }
1490 
getCharacteristics()1491     private CameraCharacteristics getCharacteristics() {
1492         return mCharacteristics;
1493     }
1494 }
1495