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