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