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