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