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.CameraDevice;
29 import android.hardware.camera2.CameraExtensionCharacteristics;
30 import android.hardware.camera2.CameraMetadata;
31 import android.hardware.camera2.CameraOfflineSession;
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.HashMap;
67 import java.util.HashSet;
68 import java.util.Iterator;
69 import java.util.List;
70 import java.util.Map;
71 import java.util.Objects;
72 import java.util.Set;
73 import java.util.concurrent.Executor;
74 import java.util.concurrent.ExecutorService;
75 import java.util.concurrent.Executors;
76 import java.util.concurrent.atomic.AtomicBoolean;
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             if (mCurrentExtensionSession != null) {
705                 mCurrentExtensionSession.commitStats();
706             }
707 
708             if (mCurrentAdvancedExtensionSession != null) {
709                 mCurrentAdvancedExtensionSession.commitStats();
710             }
711 
712             // Notify current session that it's going away, before starting camera operations
713             // After this call completes, the session is not allowed to call into CameraDeviceImpl
714             if (mCurrentSession != null) {
715                 mCurrentSession.replaceSessionClose();
716             }
717 
718             if (mCurrentExtensionSession != null) {
719                 mCurrentExtensionSession.release(false /*skipCloseNotification*/);
720                 mCurrentExtensionSession = null;
721             }
722 
723             if (mCurrentAdvancedExtensionSession != null) {
724                 mCurrentAdvancedExtensionSession.release(false /*skipCloseNotification*/);
725                 mCurrentAdvancedExtensionSession = null;
726             }
727 
728             // TODO: dont block for this
729             boolean configureSuccess = true;
730             CameraAccessException pendingException = null;
731             Surface input = null;
732             try {
733                 // configure streams and then block until IDLE
734                 configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations,
735                         operatingMode, sessionParams, createSessionStartTime);
736                 if (configureSuccess == true && inputConfig != null) {
737                     input = mRemoteDevice.getInputSurface();
738                 }
739             } catch (CameraAccessException e) {
740                 configureSuccess = false;
741                 pendingException = e;
742                 input = null;
743                 if (DEBUG) {
744                     Log.v(TAG, "createCaptureSession - failed with exception ", e);
745                 }
746             }
747 
748             // Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise.
749             CameraCaptureSessionCore newSession = null;
750             if (isConstrainedHighSpeed) {
751                 ArrayList<Surface> surfaces = new ArrayList<>(outputConfigurations.size());
752                 for (OutputConfiguration outConfig : outputConfigurations) {
753                     surfaces.add(outConfig.getSurface());
754                 }
755                 StreamConfigurationMap config =
756                     getCharacteristics().get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
757                 SurfaceUtils.checkConstrainedHighSpeedSurfaces(surfaces, /*fpsRange*/null, config);
758 
759                 newSession = new CameraConstrainedHighSpeedCaptureSessionImpl(mNextSessionId++,
760                         callback, executor, this, mDeviceExecutor, configureSuccess,
761                         mCharacteristics);
762             } else {
763                 newSession = new CameraCaptureSessionImpl(mNextSessionId++, input,
764                         callback, executor, this, mDeviceExecutor, configureSuccess);
765             }
766 
767             // TODO: wait until current session closes, then create the new session
768             mCurrentSession = newSession;
769 
770             if (pendingException != null) {
771                 throw pendingException;
772             }
773 
774             mSessionStateCallback = mCurrentSession.getDeviceStateCallback();
775         }
776     }
777 
778     @Override
isSessionConfigurationSupported( @onNull SessionConfiguration sessionConfig)779     public boolean isSessionConfigurationSupported(
780             @NonNull SessionConfiguration sessionConfig) throws CameraAccessException,
781             UnsupportedOperationException, IllegalArgumentException {
782         synchronized(mInterfaceLock) {
783             checkIfCameraClosedOrInError();
784 
785             return mRemoteDevice.isSessionConfigurationSupported(sessionConfig);
786         }
787     }
788 
789     /**
790      * For use by backwards-compatibility code only.
791      */
setSessionListener(StateCallbackKK sessionCallback)792     public void setSessionListener(StateCallbackKK sessionCallback) {
793         synchronized(mInterfaceLock) {
794             mSessionStateCallback = sessionCallback;
795         }
796     }
797 
overrideEnableZsl(CameraMetadataNative request, boolean newValue)798     private void overrideEnableZsl(CameraMetadataNative request, boolean newValue) {
799         Boolean enableZsl = request.get(CaptureRequest.CONTROL_ENABLE_ZSL);
800         if (enableZsl == null) {
801             // If enableZsl is not available, don't override.
802             return;
803         }
804 
805         request.set(CaptureRequest.CONTROL_ENABLE_ZSL, newValue);
806     }
807 
808     @Override
createCaptureRequest(int templateType, Set<String> physicalCameraIdSet)809     public CaptureRequest.Builder createCaptureRequest(int templateType,
810             Set<String> physicalCameraIdSet)
811             throws CameraAccessException {
812         synchronized(mInterfaceLock) {
813             checkIfCameraClosedOrInError();
814 
815             for (String physicalId : physicalCameraIdSet) {
816                 if (physicalId == getId()) {
817                     throw new IllegalStateException("Physical id matches the logical id!");
818                 }
819             }
820 
821             CameraMetadataNative templatedRequest = null;
822 
823             templatedRequest = mRemoteDevice.createDefaultRequest(templateType);
824 
825             // If app target SDK is older than O, or it's not a still capture template, enableZsl
826             // must be false in the default request.
827             if (mAppTargetSdkVersion < Build.VERSION_CODES.O ||
828                     templateType != TEMPLATE_STILL_CAPTURE) {
829                 overrideEnableZsl(templatedRequest, false);
830             }
831 
832             CaptureRequest.Builder builder = new CaptureRequest.Builder(
833                     templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE,
834                     getId(), physicalCameraIdSet);
835 
836             return builder;
837         }
838     }
839 
840     @Override
createCaptureRequest(int templateType)841     public CaptureRequest.Builder createCaptureRequest(int templateType)
842             throws CameraAccessException {
843         synchronized(mInterfaceLock) {
844             checkIfCameraClosedOrInError();
845 
846             CameraMetadataNative templatedRequest = null;
847 
848             templatedRequest = mRemoteDevice.createDefaultRequest(templateType);
849 
850             // If app target SDK is older than O, or it's not a still capture template, enableZsl
851             // must be false in the default request.
852             if (mAppTargetSdkVersion < Build.VERSION_CODES.O ||
853                     templateType != TEMPLATE_STILL_CAPTURE) {
854                 overrideEnableZsl(templatedRequest, false);
855             }
856 
857             CaptureRequest.Builder builder = new CaptureRequest.Builder(
858                     templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE,
859                     getId(), /*physicalCameraIdSet*/ null);
860 
861             return builder;
862         }
863     }
864 
865     @Override
createReprocessCaptureRequest(TotalCaptureResult inputResult)866     public CaptureRequest.Builder createReprocessCaptureRequest(TotalCaptureResult inputResult)
867             throws CameraAccessException {
868         synchronized(mInterfaceLock) {
869             checkIfCameraClosedOrInError();
870 
871             CameraMetadataNative resultMetadata = new
872                     CameraMetadataNative(inputResult.getNativeCopy());
873 
874             CaptureRequest.Builder builder = new CaptureRequest.Builder(resultMetadata,
875                     /*reprocess*/true, inputResult.getSessionId(), getId(),
876                     /*physicalCameraIdSet*/ null);
877             builder.set(CaptureRequest.CONTROL_CAPTURE_INTENT,
878                     CameraMetadata.CONTROL_CAPTURE_INTENT_STILL_CAPTURE);
879 
880             return builder;
881         }
882     }
883 
prepare(Surface surface)884     public void prepare(Surface surface) throws CameraAccessException {
885         if (surface == null) throw new IllegalArgumentException("Surface is null");
886 
887         synchronized(mInterfaceLock) {
888             checkIfCameraClosedOrInError();
889             int streamId = -1;
890             for (int i = 0; i < mConfiguredOutputs.size(); i++) {
891                 final List<Surface> surfaces = mConfiguredOutputs.valueAt(i).getSurfaces();
892                 if (surfaces.contains(surface)) {
893                     streamId = mConfiguredOutputs.keyAt(i);
894                     break;
895                 }
896             }
897             if (streamId == -1) {
898                 throw new IllegalArgumentException("Surface is not part of this session");
899             }
900 
901             mRemoteDevice.prepare(streamId);
902         }
903     }
904 
prepare(int maxCount, Surface surface)905     public void prepare(int maxCount, Surface surface) throws CameraAccessException {
906         if (surface == null) throw new IllegalArgumentException("Surface is null");
907         if (maxCount <= 0) throw new IllegalArgumentException("Invalid maxCount given: " +
908                 maxCount);
909 
910         synchronized(mInterfaceLock) {
911             checkIfCameraClosedOrInError();
912             int streamId = -1;
913             for (int i = 0; i < mConfiguredOutputs.size(); i++) {
914                 if (surface == mConfiguredOutputs.valueAt(i).getSurface()) {
915                     streamId = mConfiguredOutputs.keyAt(i);
916                     break;
917                 }
918             }
919             if (streamId == -1) {
920                 throw new IllegalArgumentException("Surface is not part of this session");
921             }
922 
923             mRemoteDevice.prepare2(maxCount, streamId);
924         }
925     }
926 
updateOutputConfiguration(OutputConfiguration config)927     public void updateOutputConfiguration(OutputConfiguration config)
928             throws CameraAccessException {
929         synchronized(mInterfaceLock) {
930             checkIfCameraClosedOrInError();
931             int streamId = -1;
932             for (int i = 0; i < mConfiguredOutputs.size(); i++) {
933                 if (config.getSurface() == mConfiguredOutputs.valueAt(i).getSurface()) {
934                     streamId = mConfiguredOutputs.keyAt(i);
935                     break;
936                 }
937             }
938             if (streamId == -1) {
939                 throw new IllegalArgumentException("Invalid output configuration");
940             }
941 
942             mRemoteDevice.updateOutputConfiguration(streamId, config);
943             mConfiguredOutputs.put(streamId, config);
944         }
945     }
946 
switchToOffline( @onNull Collection<Surface> offlineOutputs, @NonNull Executor executor, @NonNull CameraOfflineSession.CameraOfflineSessionCallback listener)947     public CameraOfflineSession switchToOffline(
948             @NonNull Collection<Surface> offlineOutputs, @NonNull Executor executor,
949             @NonNull CameraOfflineSession.CameraOfflineSessionCallback listener)
950             throws CameraAccessException {
951         if (offlineOutputs.isEmpty()) {
952             throw new IllegalArgumentException("Invalid offline surfaces!");
953         }
954 
955         HashSet<Integer> offlineStreamIds = new HashSet<Integer>();
956         SparseArray<OutputConfiguration> offlineConfiguredOutputs =
957                 new SparseArray<OutputConfiguration>();
958         CameraOfflineSession ret;
959 
960         synchronized(mInterfaceLock) {
961             checkIfCameraClosedOrInError();
962             if (mOfflineSessionImpl != null) {
963                 throw new IllegalStateException("Switch to offline mode already in progress");
964             }
965 
966             for (Surface surface : offlineOutputs) {
967                 int streamId = -1;
968                 for (int i = 0; i < mConfiguredOutputs.size(); i++) {
969                     if (surface == mConfiguredOutputs.valueAt(i).getSurface()) {
970                         streamId = mConfiguredOutputs.keyAt(i);
971                         offlineConfiguredOutputs.append(streamId, mConfiguredOutputs.valueAt(i));
972                         break;
973                     }
974                 }
975                 if (streamId == -1) {
976                     throw new IllegalArgumentException("Offline surface is not part of this" +
977                             " session");
978                 }
979 
980                 if (!mOfflineSupport.contains(streamId)) {
981                     throw new IllegalArgumentException("Surface: "  + surface + " does not " +
982                             " support offline mode");
983                 }
984 
985                 offlineStreamIds.add(streamId);
986             }
987             stopRepeating();
988 
989             mOfflineSessionImpl = new CameraOfflineSessionImpl(mCameraId,
990                     mCharacteristics, executor, listener, offlineConfiguredOutputs,
991                     mConfiguredInput, mConfiguredOutputs, mFrameNumberTracker, mCaptureCallbackMap,
992                     mRequestLastFrameNumbersList);
993             ret = mOfflineSessionImpl;
994 
995             mOfflineSwitchService = Executors.newSingleThreadExecutor();
996             mConfiguredOutputs.clear();
997             mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>(REQUEST_ID_NONE, null);
998             mIdle = true;
999             mCaptureCallbackMap = new SparseArray<CaptureCallbackHolder>();
1000             mBatchOutputMap = new HashMap<>();
1001             mFrameNumberTracker = new FrameNumberTracker();
1002 
1003             mCurrentSession.closeWithoutDraining();
1004             mCurrentSession = null;
1005         }
1006 
1007         mOfflineSwitchService.execute(new Runnable() {
1008             @Override
1009             public void run() {
1010                 // We cannot hold 'mInterfaceLock' during the remote IPC in 'switchToOffline'.
1011                 // The call will block until all non-offline requests are completed and/or flushed.
1012                 // The results/errors need to be handled by 'CameraDeviceCallbacks' which also sync
1013                 // on 'mInterfaceLock'.
1014                 try {
1015                     ICameraOfflineSession remoteOfflineSession = mRemoteDevice.switchToOffline(
1016                             mOfflineSessionImpl.getCallbacks(),
1017                             Arrays.stream(offlineStreamIds.toArray(
1018                                     new Integer[offlineStreamIds.size()])).mapToInt(
1019                                             Integer::intValue).toArray());
1020                     mOfflineSessionImpl.setRemoteSession(remoteOfflineSession);
1021                 } catch (CameraAccessException e) {
1022                     mOfflineSessionImpl.notifyFailedSwitch();
1023                 } finally {
1024                     mOfflineSessionImpl = null;
1025                 }
1026             }
1027         });
1028 
1029         return ret;
1030     }
1031 
supportsOfflineProcessing(Surface surface)1032     public boolean supportsOfflineProcessing(Surface surface) {
1033         if (surface == null) throw new IllegalArgumentException("Surface is null");
1034 
1035         synchronized(mInterfaceLock) {
1036             int streamId = -1;
1037             for (int i = 0; i < mConfiguredOutputs.size(); i++) {
1038                 if (surface == mConfiguredOutputs.valueAt(i).getSurface()) {
1039                     streamId = mConfiguredOutputs.keyAt(i);
1040                     break;
1041                 }
1042             }
1043             if (streamId == -1) {
1044                 throw new IllegalArgumentException("Surface is not part of this session");
1045             }
1046 
1047             return mOfflineSupport.contains(streamId);
1048         }
1049     }
1050 
tearDown(Surface surface)1051     public void tearDown(Surface surface) throws CameraAccessException {
1052         if (surface == null) throw new IllegalArgumentException("Surface is null");
1053 
1054         synchronized(mInterfaceLock) {
1055             checkIfCameraClosedOrInError();
1056             int streamId = -1;
1057             for (int i = 0; i < mConfiguredOutputs.size(); i++) {
1058                 if (surface == mConfiguredOutputs.valueAt(i).getSurface()) {
1059                     streamId = mConfiguredOutputs.keyAt(i);
1060                     break;
1061                 }
1062             }
1063             if (streamId == -1) {
1064                 throw new IllegalArgumentException("Surface is not part of this session");
1065             }
1066 
1067             mRemoteDevice.tearDown(streamId);
1068         }
1069     }
1070 
finalizeOutputConfigs(List<OutputConfiguration> outputConfigs)1071     public void finalizeOutputConfigs(List<OutputConfiguration> outputConfigs)
1072             throws CameraAccessException {
1073         if (outputConfigs == null || outputConfigs.size() == 0) {
1074             throw new IllegalArgumentException("deferred config is null or empty");
1075         }
1076 
1077         synchronized(mInterfaceLock) {
1078             checkIfCameraClosedOrInError();
1079 
1080             for (OutputConfiguration config : outputConfigs) {
1081                 int streamId = -1;
1082                 for (int i = 0; i < mConfiguredOutputs.size(); i++) {
1083                     // Have to use equal here, as createCaptureSessionByOutputConfigurations() and
1084                     // createReprocessableCaptureSessionByConfigurations() do a copy of the configs.
1085                     if (config.equals(mConfiguredOutputs.valueAt(i))) {
1086                         streamId = mConfiguredOutputs.keyAt(i);
1087                         break;
1088                     }
1089                 }
1090                 if (streamId == -1) {
1091                     throw new IllegalArgumentException("Deferred config is not part of this "
1092                             + "session");
1093                 }
1094 
1095                 if (config.getSurfaces().size() == 0) {
1096                     throw new IllegalArgumentException("The final config for stream " + streamId
1097                             + " must have at least 1 surface");
1098                 }
1099                 mRemoteDevice.finalizeOutputConfigurations(streamId, config);
1100                 mConfiguredOutputs.put(streamId, config);
1101             }
1102         }
1103     }
1104 
capture(CaptureRequest request, CaptureCallback callback, Executor executor)1105     public int capture(CaptureRequest request, CaptureCallback callback, Executor executor)
1106             throws CameraAccessException {
1107         if (DEBUG) {
1108             Log.d(TAG, "calling capture");
1109         }
1110         List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
1111         requestList.add(request);
1112         return submitCaptureRequest(requestList, callback, executor, /*streaming*/false);
1113     }
1114 
captureBurst(List<CaptureRequest> requests, CaptureCallback callback, Executor executor)1115     public int captureBurst(List<CaptureRequest> requests, CaptureCallback callback,
1116             Executor executor) throws CameraAccessException {
1117         if (requests == null || requests.isEmpty()) {
1118             throw new IllegalArgumentException("At least one request must be given");
1119         }
1120         return submitCaptureRequest(requests, callback, executor, /*streaming*/false);
1121     }
1122 
1123     /**
1124      * This method checks lastFrameNumber returned from ICameraDeviceUser methods for
1125      * starting and stopping repeating request and flushing.
1126      *
1127      * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never
1128      * sent to HAL. Then onCaptureSequenceAborted is immediately triggered.
1129      * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber as the last
1130      * regular frame number will be added to the list mRequestLastFrameNumbersList.</p>
1131      *
1132      * @param requestId the request ID of the current repeating request.
1133      * @param lastFrameNumber last frame number returned from binder.
1134      * @param repeatingRequestTypes the repeating requests' types.
1135      */
checkEarlyTriggerSequenceCompleteLocked( final int requestId, final long lastFrameNumber, final int[] repeatingRequestTypes)1136     private void checkEarlyTriggerSequenceCompleteLocked(
1137             final int requestId, final long lastFrameNumber,
1138             final int[] repeatingRequestTypes) {
1139         // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request
1140         // was never sent to HAL. Should trigger onCaptureSequenceAborted immediately.
1141         if (lastFrameNumber == CameraCaptureSession.CaptureCallback.NO_FRAMES_CAPTURED) {
1142             final CaptureCallbackHolder holder;
1143             int index = mCaptureCallbackMap.indexOfKey(requestId);
1144             holder = (index >= 0) ? mCaptureCallbackMap.valueAt(index) : null;
1145             if (holder != null) {
1146                 mCaptureCallbackMap.removeAt(index);
1147                 if (DEBUG) {
1148                     Log.v(TAG, String.format(
1149                             "remove holder for requestId %d, "
1150                             + "because lastFrame is %d.",
1151                             requestId, lastFrameNumber));
1152 
1153                     Log.v(TAG, "immediately trigger onCaptureSequenceAborted because"
1154                             + " request did not reach HAL");
1155                 }
1156 
1157                 Runnable resultDispatch = new Runnable() {
1158                     @Override
1159                     public void run() {
1160                         if (!CameraDeviceImpl.this.isClosed()) {
1161                             if (DEBUG) {
1162                                 Log.d(TAG, String.format(
1163                                         "early trigger sequence complete for request %d",
1164                                         requestId));
1165                             }
1166                             holder.getCallback().onCaptureSequenceAborted(
1167                                     CameraDeviceImpl.this,
1168                                     requestId);
1169                         }
1170                     }
1171                 };
1172                 final long ident = Binder.clearCallingIdentity();
1173                 try {
1174                     holder.getExecutor().execute(resultDispatch);
1175                 } finally {
1176                     Binder.restoreCallingIdentity(ident);
1177                 }
1178             } else {
1179                 Log.w(TAG, String.format(
1180                         "did not register callback to request %d",
1181                         requestId));
1182             }
1183         } else {
1184             // This function is only called for regular/ZslStill request so lastFrameNumber is the
1185             // last regular/ZslStill frame number.
1186             mRequestLastFrameNumbersList.add(new RequestLastFrameNumbersHolder(requestId,
1187                     lastFrameNumber, repeatingRequestTypes));
1188 
1189             // It is possible that the last frame has already arrived, so we need to check
1190             // for sequence completion right away
1191             checkAndFireSequenceComplete();
1192         }
1193     }
1194 
getRequestTypes(final CaptureRequest[] requestArray)1195     private int[] getRequestTypes(final CaptureRequest[] requestArray) {
1196         int[] requestTypes = new int[requestArray.length];
1197         for (int i = 0; i < requestArray.length; i++) {
1198             requestTypes[i] = requestArray[i].getRequestType();
1199         }
1200         return requestTypes;
1201     }
1202 
hasBatchedOutputs(List<CaptureRequest> requestList)1203     private boolean hasBatchedOutputs(List<CaptureRequest> requestList) {
1204         boolean hasBatchedOutputs = true;
1205         for (int i = 0; i < requestList.size(); i++) {
1206             CaptureRequest request = requestList.get(i);
1207             if (!request.isPartOfCRequestList()) {
1208                 hasBatchedOutputs = false;
1209                 break;
1210             }
1211             if (i == 0) {
1212                 Collection<Surface> targets = request.getTargets();
1213                 if (targets.size() != 2) {
1214                     hasBatchedOutputs = false;
1215                     break;
1216                 }
1217             }
1218         }
1219         return hasBatchedOutputs;
1220     }
1221 
updateTracker(int requestId, long frameNumber, int requestType, CaptureResult result, boolean isPartialResult)1222     private void updateTracker(int requestId, long frameNumber,
1223             int requestType, CaptureResult result, boolean isPartialResult) {
1224         int requestCount = 1;
1225         // If the request has batchedOutputs update each frame within the batch.
1226         if (mBatchOutputMap.containsKey(requestId)) {
1227             requestCount = mBatchOutputMap.get(requestId);
1228             for (int i = 0; i < requestCount; i++) {
1229                 mFrameNumberTracker.updateTracker(frameNumber - (requestCount - 1 - i),
1230                         result, isPartialResult, requestType);
1231             }
1232         } else {
1233             mFrameNumberTracker.updateTracker(frameNumber, result,
1234                     isPartialResult, requestType);
1235         }
1236     }
1237 
submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback, Executor executor, boolean repeating)1238     private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback,
1239             Executor executor, boolean repeating) throws CameraAccessException {
1240 
1241         // Need a valid executor, or current thread needs to have a looper, if
1242         // callback is valid
1243         executor = checkExecutor(executor, callback);
1244 
1245         synchronized(mInterfaceLock) {
1246             checkIfCameraClosedOrInError();
1247 
1248             // Make sure that there all requests have at least 1 surface; all surfaces are non-null;
1249             for (CaptureRequest request : requestList) {
1250                 if (request.getTargets().isEmpty()) {
1251                     throw new IllegalArgumentException(
1252                             "Each request must have at least one Surface target");
1253                 }
1254 
1255                 for (Surface surface : request.getTargets()) {
1256                     if (surface == null) {
1257                         throw new IllegalArgumentException("Null Surface targets are not allowed");
1258                     }
1259                 }
1260             }
1261 
1262             if (repeating) {
1263                 stopRepeating();
1264             }
1265 
1266             SubmitInfo requestInfo;
1267 
1268             CaptureRequest[] requestArray = requestList.toArray(new CaptureRequest[requestList.size()]);
1269             // Convert Surface to streamIdx and surfaceIdx
1270             for (CaptureRequest request : requestArray) {
1271                 request.convertSurfaceToStreamId(mConfiguredOutputs);
1272             }
1273 
1274             requestInfo = mRemoteDevice.submitRequestList(requestArray, repeating);
1275             if (DEBUG) {
1276                 Log.v(TAG, "last frame number " + requestInfo.getLastFrameNumber());
1277             }
1278 
1279             for (CaptureRequest request : requestArray) {
1280                 request.recoverStreamIdToSurface();
1281             }
1282 
1283             // If the request has batched outputs, then store the
1284             // requestCount and requestId in the map.
1285             boolean hasBatchedOutputs = hasBatchedOutputs(requestList);
1286             if (hasBatchedOutputs) {
1287                 int requestCount = requestList.size();
1288                 mBatchOutputMap.put(requestInfo.getRequestId(), requestCount);
1289             }
1290 
1291             if (callback != null) {
1292                 mCaptureCallbackMap.put(requestInfo.getRequestId(),
1293                         new CaptureCallbackHolder(
1294                             callback, requestList, executor, repeating, mNextSessionId - 1));
1295             } else {
1296                 if (DEBUG) {
1297                     Log.d(TAG, "Listen for request " + requestInfo.getRequestId() + " is null");
1298                 }
1299             }
1300 
1301             if (repeating) {
1302                 if (mRepeatingRequestId != REQUEST_ID_NONE) {
1303                     checkEarlyTriggerSequenceCompleteLocked(mRepeatingRequestId,
1304                             requestInfo.getLastFrameNumber(),
1305                             mRepeatingRequestTypes);
1306                 }
1307                 mRepeatingRequestId = requestInfo.getRequestId();
1308                 mRepeatingRequestTypes = getRequestTypes(requestArray);
1309             } else {
1310                 mRequestLastFrameNumbersList.add(
1311                     new RequestLastFrameNumbersHolder(requestList, requestInfo));
1312             }
1313 
1314             if (mIdle) {
1315                 mDeviceExecutor.execute(mCallOnActive);
1316             }
1317             mIdle = false;
1318 
1319             return requestInfo.getRequestId();
1320         }
1321     }
1322 
setRepeatingRequest(CaptureRequest request, CaptureCallback callback, Executor executor)1323     public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback,
1324             Executor executor) throws CameraAccessException {
1325         List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
1326         requestList.add(request);
1327         return submitCaptureRequest(requestList, callback, executor, /*streaming*/true);
1328     }
1329 
setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback callback, Executor executor)1330     public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback callback,
1331             Executor executor) throws CameraAccessException {
1332         if (requests == null || requests.isEmpty()) {
1333             throw new IllegalArgumentException("At least one request must be given");
1334         }
1335         return submitCaptureRequest(requests, callback, executor, /*streaming*/true);
1336     }
1337 
stopRepeating()1338     public void stopRepeating() throws CameraAccessException {
1339 
1340         synchronized(mInterfaceLock) {
1341             checkIfCameraClosedOrInError();
1342             if (mRepeatingRequestId != REQUEST_ID_NONE) {
1343 
1344                 int requestId = mRepeatingRequestId;
1345                 mRepeatingRequestId = REQUEST_ID_NONE;
1346                 mFailedRepeatingRequestId = REQUEST_ID_NONE;
1347                 int[] requestTypes = mRepeatingRequestTypes;
1348                 mRepeatingRequestTypes = null;
1349                 mFailedRepeatingRequestTypes = null;
1350 
1351                 long lastFrameNumber;
1352                 try {
1353                     lastFrameNumber = mRemoteDevice.cancelRequest(requestId);
1354                 } catch (IllegalArgumentException e) {
1355                     if (DEBUG) {
1356                         Log.v(TAG, "Repeating request was already stopped for request " +
1357                                 requestId);
1358                     }
1359                     // Cache request id and request types in case of a race with
1360                     // "onRepeatingRequestError" which may no yet be scheduled on another thread
1361                     // or blocked by us.
1362                     mFailedRepeatingRequestId = requestId;
1363                     mFailedRepeatingRequestTypes = requestTypes;
1364 
1365                     // Repeating request was already stopped. Nothing more to do.
1366                     return;
1367                 }
1368 
1369                 checkEarlyTriggerSequenceCompleteLocked(requestId, lastFrameNumber, requestTypes);
1370             }
1371         }
1372     }
1373 
waitUntilIdle()1374     private void waitUntilIdle() throws CameraAccessException {
1375 
1376         synchronized(mInterfaceLock) {
1377             checkIfCameraClosedOrInError();
1378 
1379             if (mRepeatingRequestId != REQUEST_ID_NONE) {
1380                 throw new IllegalStateException("Active repeating request ongoing");
1381             }
1382 
1383             mRemoteDevice.waitUntilIdle();
1384         }
1385     }
1386 
flush()1387     public void flush() throws CameraAccessException {
1388         synchronized(mInterfaceLock) {
1389             checkIfCameraClosedOrInError();
1390 
1391             mDeviceExecutor.execute(mCallOnBusy);
1392 
1393             // If already idle, just do a busy->idle transition immediately, don't actually
1394             // flush.
1395             if (mIdle) {
1396                 mDeviceExecutor.execute(mCallOnIdle);
1397                 return;
1398             }
1399 
1400             long lastFrameNumber = mRemoteDevice.flush();
1401             if (mRepeatingRequestId != REQUEST_ID_NONE) {
1402                 checkEarlyTriggerSequenceCompleteLocked(mRepeatingRequestId, lastFrameNumber,
1403                         mRepeatingRequestTypes);
1404                 mRepeatingRequestId = REQUEST_ID_NONE;
1405                 mRepeatingRequestTypes = null;
1406             }
1407         }
1408     }
1409 
1410     @Override
close()1411     public void close() {
1412         synchronized (mInterfaceLock) {
1413             if (mClosing.getAndSet(true)) {
1414                 return;
1415             }
1416 
1417             if (mOfflineSwitchService != null) {
1418                 mOfflineSwitchService.shutdownNow();
1419                 mOfflineSwitchService = null;
1420             }
1421 
1422             // Let extension sessions commit stats before disconnecting remoteDevice
1423             if (mCurrentExtensionSession != null) {
1424                 mCurrentExtensionSession.commitStats();
1425             }
1426 
1427             if (mCurrentAdvancedExtensionSession != null) {
1428                 mCurrentAdvancedExtensionSession.commitStats();
1429             }
1430 
1431             if (mRemoteDevice != null) {
1432                 mRemoteDevice.disconnect();
1433                 mRemoteDevice.unlinkToDeath(this, /*flags*/0);
1434             }
1435 
1436             if (mCurrentExtensionSession != null) {
1437                 mCurrentExtensionSession.release(true /*skipCloseNotification*/);
1438                 mCurrentExtensionSession = null;
1439             }
1440 
1441             if (mCurrentAdvancedExtensionSession != null) {
1442                 mCurrentAdvancedExtensionSession.release(true /*skipCloseNotification*/);
1443                 mCurrentAdvancedExtensionSession = null;
1444             }
1445 
1446             // Only want to fire the onClosed callback once;
1447             // either a normal close where the remote device is valid
1448             // or a close after a startup error (no remote device but in error state)
1449             if (mRemoteDevice != null || mInError) {
1450                 mDeviceExecutor.execute(mCallOnClosed);
1451             }
1452 
1453             mRemoteDevice = null;
1454         }
1455     }
1456 
1457     @Override
finalize()1458     protected void finalize() throws Throwable {
1459         try {
1460             close();
1461         }
1462         finally {
1463             super.finalize();
1464         }
1465     }
1466 
checkInputConfigurationWithStreamConfigurationsAs( InputConfiguration inputConfig, StreamConfigurationMap configMap)1467     private boolean checkInputConfigurationWithStreamConfigurationsAs(
1468             InputConfiguration inputConfig, StreamConfigurationMap configMap) {
1469         int[] inputFormats = configMap.getInputFormats();
1470         boolean validFormat = false;
1471         int inputFormat = inputConfig.getFormat();
1472         for (int format : inputFormats) {
1473             if (format == inputFormat) {
1474                 validFormat = true;
1475             }
1476         }
1477 
1478         // Allow RAW formats, even when not advertised.
1479         if (inputFormat == ImageFormat.RAW_PRIVATE || inputFormat == ImageFormat.RAW10
1480                 || inputFormat == ImageFormat.RAW12 || inputFormat == ImageFormat.RAW_SENSOR) {
1481             return true;
1482         }
1483 
1484         if (validFormat == false) {
1485             return false;
1486         }
1487 
1488         boolean validSize = false;
1489         Size[] inputSizes = configMap.getInputSizes(inputFormat);
1490         for (Size s : inputSizes) {
1491             if (inputConfig.getWidth() == s.getWidth() &&
1492                     inputConfig.getHeight() == s.getHeight()) {
1493                 validSize = true;
1494             }
1495         }
1496 
1497         if (validSize == false) {
1498             return false;
1499         }
1500         return true;
1501     }
1502 
checkInputConfigurationWithStreamConfigurations( InputConfiguration inputConfig, boolean maxResolution)1503     private boolean checkInputConfigurationWithStreamConfigurations(
1504             InputConfiguration inputConfig, boolean maxResolution) {
1505         // Check if either this logical camera or any of its physical cameras support the
1506         // input config. If they do, the input config is valid.
1507         CameraCharacteristics.Key<StreamConfigurationMap> ck =
1508                 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP;
1509 
1510         if (maxResolution) {
1511             ck = CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION;
1512         }
1513 
1514         StreamConfigurationMap configMap = mCharacteristics.get(ck);
1515 
1516         if (configMap != null &&
1517                 checkInputConfigurationWithStreamConfigurationsAs(inputConfig, configMap)) {
1518             return true;
1519         }
1520 
1521         for (Map.Entry<String, CameraCharacteristics> entry : mPhysicalIdsToChars.entrySet()) {
1522             configMap = entry.getValue().get(ck);
1523 
1524             if (configMap != null &&
1525                     checkInputConfigurationWithStreamConfigurationsAs(inputConfig, configMap)) {
1526                 // Input config supported.
1527                 return true;
1528             }
1529         }
1530         return false;
1531     }
1532 
checkInputConfiguration(InputConfiguration inputConfig)1533     private void checkInputConfiguration(InputConfiguration inputConfig) {
1534         if (inputConfig == null) {
1535             return;
1536         }
1537         int inputFormat = inputConfig.getFormat();
1538         if (inputConfig.isMultiResolution()) {
1539             MultiResolutionStreamConfigurationMap configMap = mCharacteristics.get(
1540                     CameraCharacteristics.SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP);
1541 
1542             int[] inputFormats = configMap.getInputFormats();
1543             boolean validFormat = false;
1544             for (int format : inputFormats) {
1545                 if (format == inputFormat) {
1546                     validFormat = true;
1547                 }
1548             }
1549 
1550             if (validFormat == false) {
1551                 throw new IllegalArgumentException("multi-resolution input format " +
1552                         inputFormat + " is not valid");
1553             }
1554 
1555             boolean validSize = false;
1556             Collection<MultiResolutionStreamInfo> inputStreamInfo =
1557                     configMap.getInputInfo(inputFormat);
1558             for (MultiResolutionStreamInfo info : inputStreamInfo) {
1559                 if (inputConfig.getWidth() == info.getWidth() &&
1560                         inputConfig.getHeight() == info.getHeight()) {
1561                     validSize = true;
1562                 }
1563             }
1564 
1565             if (validSize == false) {
1566                 throw new IllegalArgumentException("Multi-resolution input size " +
1567                         inputConfig.getWidth() + "x" + inputConfig.getHeight() + " is not valid");
1568             }
1569         } else {
1570             if (!checkInputConfigurationWithStreamConfigurations(inputConfig, /*maxRes*/false) &&
1571                     !checkInputConfigurationWithStreamConfigurations(inputConfig, /*maxRes*/true)) {
1572                 throw new IllegalArgumentException("Input config with format " +
1573                         inputFormat + " and size " + inputConfig.getWidth() + "x" +
1574                         inputConfig.getHeight() + " not supported by camera id " + mCameraId);
1575             }
1576         }
1577     }
1578 
1579     /**
1580      * A callback for notifications about the state of a camera device, adding in the callbacks that
1581      * were part of the earlier KK API design, but now only used internally.
1582      */
1583     public static abstract class StateCallbackKK extends StateCallback {
1584         /**
1585          * The method called when a camera device has no outputs configured.
1586          *
1587          */
onUnconfigured(CameraDevice camera)1588         public void onUnconfigured(CameraDevice camera) {
1589             // Default empty implementation
1590         }
1591 
1592         /**
1593          * The method called when a camera device begins processing
1594          * {@link CaptureRequest capture requests}.
1595          *
1596          */
onActive(CameraDevice camera)1597         public void onActive(CameraDevice camera) {
1598             // Default empty implementation
1599         }
1600 
1601         /**
1602          * The method called when a camera device is busy.
1603          *
1604          */
onBusy(CameraDevice camera)1605         public void onBusy(CameraDevice camera) {
1606             // Default empty implementation
1607         }
1608 
1609         /**
1610          * The method called when a camera device has finished processing all
1611          * submitted capture requests and has reached an idle state.
1612          *
1613          */
onIdle(CameraDevice camera)1614         public void onIdle(CameraDevice camera) {
1615             // Default empty implementation
1616         }
1617 
1618         /**
1619          * This method is called when camera device's non-repeating request queue is empty,
1620          * and is ready to start capturing next image.
1621          */
onRequestQueueEmpty()1622         public void onRequestQueueEmpty() {
1623             // Default empty implementation
1624         }
1625 
1626         /**
1627          * The method called when the camera device has finished preparing
1628          * an output Surface
1629          */
onSurfacePrepared(Surface surface)1630         public void onSurfacePrepared(Surface surface) {
1631             // Default empty implementation
1632         }
1633     }
1634 
checkAndFireSequenceComplete()1635     private void checkAndFireSequenceComplete() {
1636         long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
1637         long completedReprocessFrameNumber = mFrameNumberTracker.getCompletedReprocessFrameNumber();
1638         long completedZslStillFrameNumber = mFrameNumberTracker.getCompletedZslStillFrameNumber();
1639 
1640         Iterator<RequestLastFrameNumbersHolder> iter = mRequestLastFrameNumbersList.iterator();
1641         while (iter.hasNext()) {
1642             final RequestLastFrameNumbersHolder requestLastFrameNumbers = iter.next();
1643             final int requestId = requestLastFrameNumbers.getRequestId();
1644             final CaptureCallbackHolder holder;
1645             if (mRemoteDevice == null) {
1646                 Log.w(TAG, "Camera closed while checking sequences");
1647                 return;
1648             }
1649             if (!requestLastFrameNumbers.isSequenceCompleted()) {
1650                 long lastRegularFrameNumber =
1651                         requestLastFrameNumbers.getLastRegularFrameNumber();
1652                 long lastReprocessFrameNumber =
1653                         requestLastFrameNumbers.getLastReprocessFrameNumber();
1654                 long lastZslStillFrameNumber =
1655                         requestLastFrameNumbers.getLastZslStillFrameNumber();
1656                 if (lastRegularFrameNumber <= completedFrameNumber
1657                         && lastReprocessFrameNumber <= completedReprocessFrameNumber
1658                         && lastZslStillFrameNumber <= completedZslStillFrameNumber) {
1659                     if (DEBUG) {
1660                         Log.v(TAG, String.format(
1661                                 "Mark requestId %d as completed, because lastRegularFrame %d "
1662                                 + "is <= %d, lastReprocessFrame %d is <= %d, "
1663                                 + "lastZslStillFrame %d is <= %d", requestId,
1664                                 lastRegularFrameNumber, completedFrameNumber,
1665                                 lastReprocessFrameNumber, completedReprocessFrameNumber,
1666                                 lastZslStillFrameNumber, completedZslStillFrameNumber));
1667                     }
1668                     requestLastFrameNumbers.markSequenceCompleted();
1669                 }
1670 
1671                 // Call onCaptureSequenceCompleted
1672                 int index = mCaptureCallbackMap.indexOfKey(requestId);
1673                 holder = (index >= 0) ?
1674                         mCaptureCallbackMap.valueAt(index) : null;
1675                 if (holder != null && requestLastFrameNumbers.isSequenceCompleted()) {
1676                     Runnable resultDispatch = new Runnable() {
1677                         @Override
1678                         public void run() {
1679                             if (!CameraDeviceImpl.this.isClosed()){
1680                                 if (DEBUG) {
1681                                     Log.d(TAG, String.format(
1682                                             "fire sequence complete for request %d",
1683                                             requestId));
1684                                 }
1685 
1686                                 holder.getCallback().onCaptureSequenceCompleted(
1687                                     CameraDeviceImpl.this,
1688                                     requestId,
1689                                     requestLastFrameNumbers.getLastFrameNumber());
1690                             }
1691                         }
1692                     };
1693                     final long ident = Binder.clearCallingIdentity();
1694                     try {
1695                         holder.getExecutor().execute(resultDispatch);
1696                     } finally {
1697                         Binder.restoreCallingIdentity(ident);
1698                     }
1699                 }
1700             }
1701 
1702             if (requestLastFrameNumbers.isSequenceCompleted() &&
1703                     requestLastFrameNumbers.isInflightCompleted()) {
1704                 int index = mCaptureCallbackMap.indexOfKey(requestId);
1705                 if (index >= 0) {
1706                     mCaptureCallbackMap.removeAt(index);
1707                 }
1708                 if (DEBUG) {
1709                     Log.v(TAG, String.format(
1710                             "Remove holder for requestId %d", requestId));
1711                 }
1712                 iter.remove();
1713             }
1714         }
1715     }
1716 
removeCompletedCallbackHolderLocked(long lastCompletedRegularFrameNumber, long lastCompletedReprocessFrameNumber, long lastCompletedZslStillFrameNumber)1717     private void removeCompletedCallbackHolderLocked(long lastCompletedRegularFrameNumber,
1718             long lastCompletedReprocessFrameNumber, long lastCompletedZslStillFrameNumber) {
1719         if (DEBUG) {
1720             Log.v(TAG, String.format("remove completed callback holders for "
1721                     + "lastCompletedRegularFrameNumber %d, "
1722                     + "lastCompletedReprocessFrameNumber %d, "
1723                     + "lastCompletedZslStillFrameNumber %d",
1724                     lastCompletedRegularFrameNumber,
1725                     lastCompletedReprocessFrameNumber,
1726                     lastCompletedZslStillFrameNumber));
1727         }
1728 
1729         Iterator<RequestLastFrameNumbersHolder> iter = mRequestLastFrameNumbersList.iterator();
1730         while (iter.hasNext()) {
1731             final RequestLastFrameNumbersHolder requestLastFrameNumbers = iter.next();
1732             final int requestId = requestLastFrameNumbers.getRequestId();
1733             final CaptureCallbackHolder holder;
1734             if (mRemoteDevice == null) {
1735                 Log.w(TAG, "Camera closed while removing completed callback holders");
1736                 return;
1737             }
1738 
1739             long lastRegularFrameNumber =
1740                     requestLastFrameNumbers.getLastRegularFrameNumber();
1741             long lastReprocessFrameNumber =
1742                     requestLastFrameNumbers.getLastReprocessFrameNumber();
1743             long lastZslStillFrameNumber =
1744                     requestLastFrameNumbers.getLastZslStillFrameNumber();
1745 
1746             if (lastRegularFrameNumber <= lastCompletedRegularFrameNumber
1747                         && lastReprocessFrameNumber <= lastCompletedReprocessFrameNumber
1748                         && lastZslStillFrameNumber <= lastCompletedZslStillFrameNumber) {
1749 
1750                 if (requestLastFrameNumbers.isSequenceCompleted()) {
1751                     int index = mCaptureCallbackMap.indexOfKey(requestId);
1752                     if (index >= 0) {
1753                         mCaptureCallbackMap.removeAt(index);
1754                     }
1755                     if (DEBUG) {
1756                         Log.v(TAG, String.format(
1757                                 "Remove holder for requestId %d, because lastRegularFrame %d "
1758                                 + "is <= %d, lastReprocessFrame %d is <= %d, "
1759                                 + "lastZslStillFrame %d is <= %d", requestId,
1760                                 lastRegularFrameNumber, lastCompletedRegularFrameNumber,
1761                                 lastReprocessFrameNumber, lastCompletedReprocessFrameNumber,
1762                                 lastZslStillFrameNumber, lastCompletedZslStillFrameNumber));
1763                     }
1764                     iter.remove();
1765                 } else {
1766                     if (DEBUG) {
1767                         Log.v(TAG, "Sequence not yet completed for request id " + requestId);
1768                     }
1769                     requestLastFrameNumbers.markInflightCompleted();
1770                 }
1771             }
1772         }
1773     }
1774 
onDeviceError(final int errorCode, CaptureResultExtras resultExtras)1775     public void onDeviceError(final int errorCode, CaptureResultExtras resultExtras) {
1776         if (DEBUG) {
1777             Log.d(TAG, String.format(
1778                     "Device error received, code %d, frame number %d, request ID %d, subseq ID %d",
1779                     errorCode, resultExtras.getFrameNumber(), resultExtras.getRequestId(),
1780                     resultExtras.getSubsequenceId()));
1781         }
1782 
1783         synchronized(mInterfaceLock) {
1784             if (mRemoteDevice == null && mRemoteDeviceInit) {
1785                 return; // Camera already closed, user is not interested in errors anymore.
1786             }
1787 
1788             // Redirect device callback to the offline session in case we are in the middle
1789             // of an offline switch
1790             if (mOfflineSessionImpl != null) {
1791                 mOfflineSessionImpl.getCallbacks().onDeviceError(errorCode, resultExtras);
1792                 return;
1793             }
1794 
1795             switch (errorCode) {
1796                 case CameraDeviceCallbacks.ERROR_CAMERA_DISCONNECTED: {
1797                     final long ident = Binder.clearCallingIdentity();
1798                     try {
1799                         mDeviceExecutor.execute(mCallOnDisconnected);
1800                     } finally {
1801                         Binder.restoreCallingIdentity(ident);
1802                     }
1803                     break;
1804                 }
1805                 case CameraDeviceCallbacks.ERROR_CAMERA_REQUEST:
1806                 case CameraDeviceCallbacks.ERROR_CAMERA_RESULT:
1807                 case CameraDeviceCallbacks.ERROR_CAMERA_BUFFER:
1808                     onCaptureErrorLocked(errorCode, resultExtras);
1809                     break;
1810                 case CameraDeviceCallbacks.ERROR_CAMERA_DEVICE:
1811                     scheduleNotifyError(StateCallback.ERROR_CAMERA_DEVICE);
1812                     break;
1813                 case CameraDeviceCallbacks.ERROR_CAMERA_DISABLED:
1814                     scheduleNotifyError(StateCallback.ERROR_CAMERA_DISABLED);
1815                     break;
1816                 default:
1817                     Log.e(TAG, "Unknown error from camera device: " + errorCode);
1818                     scheduleNotifyError(StateCallback.ERROR_CAMERA_SERVICE);
1819             }
1820         }
1821     }
1822 
scheduleNotifyError(int code)1823     private void scheduleNotifyError(int code) {
1824         mInError = true;
1825         final long ident = Binder.clearCallingIdentity();
1826         try {
1827             mDeviceExecutor.execute(obtainRunnable(
1828                         CameraDeviceImpl::notifyError, this, code).recycleOnUse());
1829         } finally {
1830             Binder.restoreCallingIdentity(ident);
1831         }
1832     }
1833 
notifyError(int code)1834     private void notifyError(int code) {
1835         if (!CameraDeviceImpl.this.isClosed()) {
1836             mDeviceCallback.onError(CameraDeviceImpl.this, code);
1837         }
1838     }
1839 
1840     /**
1841      * Called by onDeviceError for handling single-capture failures.
1842      */
onCaptureErrorLocked(int errorCode, CaptureResultExtras resultExtras)1843     private void onCaptureErrorLocked(int errorCode, CaptureResultExtras resultExtras) {
1844 
1845         final int requestId = resultExtras.getRequestId();
1846         final int subsequenceId = resultExtras.getSubsequenceId();
1847         final long frameNumber = resultExtras.getFrameNumber();
1848         final String errorPhysicalCameraId = resultExtras.getErrorPhysicalCameraId();
1849         final CaptureCallbackHolder holder = mCaptureCallbackMap.get(requestId);
1850 
1851         if (holder == null) {
1852             Log.e(TAG, String.format("Receive capture error on unknown request ID %d",
1853                     requestId));
1854             return;
1855         }
1856 
1857         final CaptureRequest request = holder.getRequest(subsequenceId);
1858 
1859         Runnable failureDispatch = null;
1860         if (errorCode == CameraDeviceCallbacks.ERROR_CAMERA_BUFFER) {
1861             // Because 1 stream id could map to multiple surfaces, we need to specify both
1862             // streamId and surfaceId.
1863             OutputConfiguration config = mConfiguredOutputs.get(
1864                     resultExtras.getErrorStreamId());
1865             if (config == null) {
1866                 Log.v(TAG, String.format(
1867                         "Stream %d has been removed. Skipping buffer lost callback",
1868                         resultExtras.getErrorStreamId()));
1869                 return;
1870             }
1871             for (Surface surface : config.getSurfaces()) {
1872                 if (!request.containsTarget(surface)) {
1873                     continue;
1874                 }
1875                 if (DEBUG) {
1876                     Log.v(TAG, String.format(
1877                             "Lost output buffer reported for frame %d, target %s",
1878                             frameNumber, surface));
1879                 }
1880                 failureDispatch = new Runnable() {
1881                     @Override
1882                     public void run() {
1883                         if (!isClosed()){
1884                             holder.getCallback().onCaptureBufferLost(CameraDeviceImpl.this, request,
1885                                     surface, frameNumber);
1886                         }
1887                     }
1888                 };
1889                 // Dispatch the failure callback
1890                 final long ident = Binder.clearCallingIdentity();
1891                 try {
1892                     holder.getExecutor().execute(failureDispatch);
1893                 } finally {
1894                     Binder.restoreCallingIdentity(ident);
1895                 }
1896             }
1897         } else {
1898             boolean mayHaveBuffers = (errorCode == CameraDeviceCallbacks.ERROR_CAMERA_RESULT);
1899 
1900             // This is only approximate - exact handling needs the camera service and HAL to
1901             // disambiguate between request failures to due abort and due to real errors.  For
1902             // now, assume that if the session believes we're mid-abort, then the error is due
1903             // to abort.
1904             int reason = (mCurrentSession != null && mCurrentSession.isAborting()) ?
1905                     CaptureFailure.REASON_FLUSHED :
1906                     CaptureFailure.REASON_ERROR;
1907 
1908             final CaptureFailure failure = new CaptureFailure(
1909                 request,
1910                 reason,
1911                 mayHaveBuffers,
1912                 requestId,
1913                 frameNumber,
1914                 errorPhysicalCameraId);
1915 
1916             failureDispatch = new Runnable() {
1917                 @Override
1918                 public void run() {
1919                     if (!isClosed()){
1920                         holder.getCallback().onCaptureFailed(CameraDeviceImpl.this, request,
1921                                 failure);
1922                     }
1923                 }
1924             };
1925 
1926             // Fire onCaptureSequenceCompleted if appropriate
1927             if (DEBUG) {
1928                 Log.v(TAG, String.format("got error frame %d", frameNumber));
1929             }
1930 
1931             // Do not update frame number tracker for physical camera result error.
1932             if (errorPhysicalCameraId == null) {
1933                 // Update FrameNumberTracker for every frame during HFR mode.
1934                 if (mBatchOutputMap.containsKey(requestId)) {
1935                     for (int i = 0; i < mBatchOutputMap.get(requestId); i++) {
1936                         mFrameNumberTracker.updateTracker(frameNumber - (subsequenceId - i),
1937                                 /*error*/true, request.getRequestType());
1938                     }
1939                 } else {
1940                     mFrameNumberTracker.updateTracker(frameNumber,
1941                             /*error*/true, request.getRequestType());
1942                 }
1943 
1944                 checkAndFireSequenceComplete();
1945             }
1946 
1947             // Dispatch the failure callback
1948             final long ident = Binder.clearCallingIdentity();
1949             try {
1950                 holder.getExecutor().execute(failureDispatch);
1951             } finally {
1952                 Binder.restoreCallingIdentity(ident);
1953             }
1954         }
1955 
1956     }
1957 
onDeviceIdle()1958     public void onDeviceIdle() {
1959         if (DEBUG) {
1960             Log.d(TAG, "Camera now idle");
1961         }
1962         synchronized(mInterfaceLock) {
1963             if (mRemoteDevice == null) return; // Camera already closed
1964 
1965             // Redirect device callback to the offline session in case we are in the middle
1966             // of an offline switch
1967             if (mOfflineSessionImpl != null) {
1968                 mOfflineSessionImpl.getCallbacks().onDeviceIdle();
1969                 return;
1970             }
1971 
1972             // Remove all capture callbacks now that device has gone to IDLE state.
1973             removeCompletedCallbackHolderLocked(
1974                     Long.MAX_VALUE, /*lastCompletedRegularFrameNumber*/
1975                     Long.MAX_VALUE, /*lastCompletedReprocessFrameNumber*/
1976                     Long.MAX_VALUE /*lastCompletedZslStillFrameNumber*/);
1977 
1978             if (!CameraDeviceImpl.this.mIdle) {
1979                 final long ident = Binder.clearCallingIdentity();
1980                 try {
1981                     mDeviceExecutor.execute(mCallOnIdle);
1982                 } finally {
1983                     Binder.restoreCallingIdentity(ident);
1984                 }
1985             }
1986             mIdle = true;
1987         }
1988     }
1989 
1990     public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
1991 
1992         @Override
asBinder()1993         public IBinder asBinder() {
1994             return this;
1995         }
1996 
1997         @Override
onDeviceError(final int errorCode, CaptureResultExtras resultExtras)1998         public void onDeviceError(final int errorCode, CaptureResultExtras resultExtras) {
1999             CameraDeviceImpl.this.onDeviceError(errorCode, resultExtras);
2000         }
2001 
2002         @Override
onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId)2003         public void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId) {
2004             if (DEBUG) {
2005                 Log.d(TAG, "Repeating request error received. Last frame number is " +
2006                         lastFrameNumber);
2007             }
2008 
2009             synchronized(mInterfaceLock) {
2010                 // Camera is already closed or no repeating request is present.
2011                 if (mRemoteDevice == null || mRepeatingRequestId == REQUEST_ID_NONE) {
2012                     if ((mFailedRepeatingRequestId == repeatingRequestId) &&
2013                             (mFailedRepeatingRequestTypes != null) && (mRemoteDevice != null)) {
2014                         Log.v(TAG, "Resuming stop of failed repeating request with id: " +
2015                                 mFailedRepeatingRequestId);
2016 
2017                         checkEarlyTriggerSequenceCompleteLocked(mFailedRepeatingRequestId,
2018                                 lastFrameNumber, mFailedRepeatingRequestTypes);
2019                         mFailedRepeatingRequestId = REQUEST_ID_NONE;
2020                         mFailedRepeatingRequestTypes = null;
2021                     }
2022                     return;
2023                 }
2024 
2025                 // Redirect device callback to the offline session in case we are in the middle
2026                 // of an offline switch
2027                 if (mOfflineSessionImpl != null) {
2028                     mOfflineSessionImpl.getCallbacks().onRepeatingRequestError(
2029                            lastFrameNumber, repeatingRequestId);
2030                     return;
2031                 }
2032 
2033                 checkEarlyTriggerSequenceCompleteLocked(mRepeatingRequestId, lastFrameNumber,
2034                         mRepeatingRequestTypes);
2035                 // Check if there is already a new repeating request
2036                 if (mRepeatingRequestId == repeatingRequestId) {
2037                     mRepeatingRequestId = REQUEST_ID_NONE;
2038                     mRepeatingRequestTypes = null;
2039                 }
2040             }
2041         }
2042 
2043         @Override
onDeviceIdle()2044         public void onDeviceIdle() {
2045             CameraDeviceImpl.this.onDeviceIdle();
2046         }
2047 
2048         @Override
onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp)2049         public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) {
2050             int requestId = resultExtras.getRequestId();
2051             final long frameNumber = resultExtras.getFrameNumber();
2052             final long lastCompletedRegularFrameNumber =
2053                     resultExtras.getLastCompletedRegularFrameNumber();
2054             final long lastCompletedReprocessFrameNumber =
2055                     resultExtras.getLastCompletedReprocessFrameNumber();
2056             final long lastCompletedZslFrameNumber =
2057                     resultExtras.getLastCompletedZslFrameNumber();
2058             final boolean hasReadoutTimestamp = resultExtras.hasReadoutTimestamp();
2059             final long readoutTimestamp = resultExtras.getReadoutTimestamp();
2060 
2061             if (DEBUG) {
2062                 Log.d(TAG, "Capture started for id " + requestId + " frame number " + frameNumber
2063                         + ": completedRegularFrameNumber " + lastCompletedRegularFrameNumber
2064                         + ", completedReprocessFrameNUmber " + lastCompletedReprocessFrameNumber
2065                         + ", completedZslFrameNumber " + lastCompletedZslFrameNumber
2066                         + ", hasReadoutTimestamp " + hasReadoutTimestamp
2067                         + (hasReadoutTimestamp ? ", readoutTimestamp " + readoutTimestamp : "")) ;
2068             }
2069             final CaptureCallbackHolder holder;
2070 
2071             synchronized(mInterfaceLock) {
2072                 if (mRemoteDevice == null) return; // Camera already closed
2073 
2074 
2075                 // Redirect device callback to the offline session in case we are in the middle
2076                 // of an offline switch
2077                 if (mOfflineSessionImpl != null) {
2078                     mOfflineSessionImpl.getCallbacks().onCaptureStarted(resultExtras,
2079                             timestamp);
2080                     return;
2081                 }
2082 
2083                 // Check if it's okay to remove completed callbacks from mCaptureCallbackMap.
2084                 // A callback is completed if the corresponding inflight request has been removed
2085                 // from the inflight queue in cameraservice.
2086                 removeCompletedCallbackHolderLocked(lastCompletedRegularFrameNumber,
2087                         lastCompletedReprocessFrameNumber, lastCompletedZslFrameNumber);
2088 
2089                 // Get the callback for this frame ID, if there is one
2090                 holder = CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId);
2091 
2092                 if (holder == null) {
2093                     return;
2094                 }
2095 
2096                 if (isClosed()) return;
2097 
2098                 // Dispatch capture start notice
2099                 final long ident = Binder.clearCallingIdentity();
2100                 try {
2101                     holder.getExecutor().execute(
2102                         new Runnable() {
2103                             @Override
2104                             public void run() {
2105                                 if (!CameraDeviceImpl.this.isClosed()) {
2106                                     final int subsequenceId = resultExtras.getSubsequenceId();
2107                                     final CaptureRequest request = holder.getRequest(subsequenceId);
2108 
2109                                     if (holder.hasBatchedOutputs()) {
2110                                         // Send derived onCaptureStarted for requests within the
2111                                         // batch
2112                                         final Range<Integer> fpsRange =
2113                                             request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
2114                                         for (int i = 0; i < holder.getRequestCount(); i++) {
2115                                             holder.getCallback().onCaptureStarted(
2116                                                 CameraDeviceImpl.this,
2117                                                 holder.getRequest(i),
2118                                                 timestamp - (subsequenceId - i) *
2119                                                 NANO_PER_SECOND / fpsRange.getUpper(),
2120                                                 frameNumber - (subsequenceId - i));
2121                                             if (hasReadoutTimestamp) {
2122                                                 holder.getCallback().onReadoutStarted(
2123                                                     CameraDeviceImpl.this,
2124                                                     holder.getRequest(i),
2125                                                     readoutTimestamp - (subsequenceId - i) *
2126                                                     NANO_PER_SECOND / fpsRange.getUpper(),
2127                                                     frameNumber - (subsequenceId - i));
2128                                             }
2129                                         }
2130                                     } else {
2131                                         holder.getCallback().onCaptureStarted(
2132                                             CameraDeviceImpl.this, request,
2133                                             timestamp, frameNumber);
2134                                         if (hasReadoutTimestamp) {
2135                                             holder.getCallback().onReadoutStarted(
2136                                                 CameraDeviceImpl.this, request,
2137                                                 readoutTimestamp, frameNumber);
2138                                         }
2139                                     }
2140                                 }
2141                             }
2142                         });
2143                 } finally {
2144                     Binder.restoreCallingIdentity(ident);
2145                 }
2146             }
2147         }
2148 
2149         @Override
onResultReceived(CameraMetadataNative result, CaptureResultExtras resultExtras, PhysicalCaptureResultInfo physicalResults[])2150         public void onResultReceived(CameraMetadataNative result,
2151                 CaptureResultExtras resultExtras, PhysicalCaptureResultInfo physicalResults[])
2152                 throws RemoteException {
2153             int requestId = resultExtras.getRequestId();
2154             long frameNumber = resultExtras.getFrameNumber();
2155 
2156             if (DEBUG) {
2157                 Log.v(TAG, "Received result frame " + frameNumber + " for id "
2158                         + requestId);
2159             }
2160 
2161             synchronized(mInterfaceLock) {
2162                 if (mRemoteDevice == null) return; // Camera already closed
2163 
2164 
2165                 // Redirect device callback to the offline session in case we are in the middle
2166                 // of an offline switch
2167                 if (mOfflineSessionImpl != null) {
2168                     mOfflineSessionImpl.getCallbacks().onResultReceived(result, resultExtras,
2169                             physicalResults);
2170                     return;
2171                 }
2172 
2173                 // TODO: Handle CameraCharacteristics access from CaptureResult correctly.
2174                 result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE,
2175                         getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE));
2176 
2177                 final CaptureCallbackHolder holder =
2178                         CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId);
2179 
2180                 boolean isPartialResult =
2181                         (resultExtras.getPartialResultCount() < mTotalPartialCount);
2182 
2183                 // Check if we have a callback for this
2184                 if (holder == null) {
2185                     if (DEBUG) {
2186                         Log.d(TAG,
2187                                 "holder is null, early return at frame "
2188                                         + frameNumber);
2189                     }
2190 
2191                     return;
2192                 }
2193 
2194                 final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
2195                 int requestType = request.getRequestType();
2196                 if (isClosed()) {
2197                     if (DEBUG) {
2198                         Log.d(TAG,
2199                                 "camera is closed, early return at frame "
2200                                         + frameNumber);
2201                     }
2202 
2203                     updateTracker(requestId, frameNumber, requestType, /*result*/null,
2204                             isPartialResult);
2205 
2206                     return;
2207                 }
2208 
2209 
2210                 Runnable resultDispatch = null;
2211 
2212                 CaptureResult finalResult;
2213                 // Make a copy of the native metadata before it gets moved to a CaptureResult
2214                 // object.
2215                 final CameraMetadataNative resultCopy;
2216                 if (holder.hasBatchedOutputs()) {
2217                     resultCopy = new CameraMetadataNative(result);
2218                 } else {
2219                     resultCopy = null;
2220                 }
2221 
2222                 // Either send a partial result or the final capture completed result
2223                 if (isPartialResult) {
2224                     final CaptureResult resultAsCapture =
2225                             new CaptureResult(getId(), result, request, resultExtras);
2226                     // Partial result
2227                     resultDispatch = new Runnable() {
2228                         @Override
2229                         public void run() {
2230                             if (!CameraDeviceImpl.this.isClosed()) {
2231                                 if (holder.hasBatchedOutputs()) {
2232                                     // Send derived onCaptureProgressed for requests within
2233                                     // the batch.
2234                                     for (int i = 0; i < holder.getRequestCount(); i++) {
2235                                         CameraMetadataNative resultLocal =
2236                                                 new CameraMetadataNative(resultCopy);
2237                                         CaptureResult resultInBatch = new CaptureResult(getId(),
2238                                                 resultLocal, holder.getRequest(i), resultExtras);
2239 
2240                                         holder.getCallback().onCaptureProgressed(
2241                                             CameraDeviceImpl.this,
2242                                             holder.getRequest(i),
2243                                             resultInBatch);
2244                                     }
2245                                 } else {
2246                                     holder.getCallback().onCaptureProgressed(
2247                                         CameraDeviceImpl.this,
2248                                         request,
2249                                         resultAsCapture);
2250                                 }
2251                             }
2252                         }
2253                     };
2254                     finalResult = resultAsCapture;
2255                 } else {
2256                     List<CaptureResult> partialResults =
2257                             mFrameNumberTracker.popPartialResults(frameNumber);
2258                     if (mBatchOutputMap.containsKey(requestId)) {
2259                         int requestCount = mBatchOutputMap.get(requestId);
2260                         for (int i = 1; i < requestCount; i++) {
2261                             mFrameNumberTracker.popPartialResults(frameNumber - (requestCount - i));
2262                         }
2263                     }
2264 
2265                     final long sensorTimestamp =
2266                             result.get(CaptureResult.SENSOR_TIMESTAMP);
2267                     final Range<Integer> fpsRange =
2268                             request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
2269                     final int subsequenceId = resultExtras.getSubsequenceId();
2270                     final TotalCaptureResult resultAsCapture = new TotalCaptureResult(getId(),
2271                             result, request, resultExtras, partialResults, holder.getSessionId(),
2272                             physicalResults);
2273                     // Final capture result
2274                     resultDispatch = new Runnable() {
2275                         @Override
2276                         public void run() {
2277                             if (!CameraDeviceImpl.this.isClosed()){
2278                                 if (holder.hasBatchedOutputs()) {
2279                                     // Send derived onCaptureCompleted for requests within
2280                                     // the batch.
2281                                     for (int i = 0; i < holder.getRequestCount(); i++) {
2282                                         resultCopy.set(CaptureResult.SENSOR_TIMESTAMP,
2283                                                 sensorTimestamp - (subsequenceId - i) *
2284                                                 NANO_PER_SECOND/fpsRange.getUpper());
2285                                         CameraMetadataNative resultLocal =
2286                                                 new CameraMetadataNative(resultCopy);
2287                                         // No logical multi-camera support for batched output mode.
2288                                         TotalCaptureResult resultInBatch = new TotalCaptureResult(
2289                                                 getId(), resultLocal, holder.getRequest(i),
2290                                                 resultExtras, partialResults, holder.getSessionId(),
2291                                                 new PhysicalCaptureResultInfo[0]);
2292 
2293                                         holder.getCallback().onCaptureCompleted(
2294                                             CameraDeviceImpl.this,
2295                                             holder.getRequest(i),
2296                                             resultInBatch);
2297                                     }
2298                                 } else {
2299                                     holder.getCallback().onCaptureCompleted(
2300                                         CameraDeviceImpl.this,
2301                                         request,
2302                                         resultAsCapture);
2303                                 }
2304                             }
2305                         }
2306                     };
2307                     finalResult = resultAsCapture;
2308                 }
2309 
2310                 final long ident = Binder.clearCallingIdentity();
2311                 try {
2312                     holder.getExecutor().execute(resultDispatch);
2313                 } finally {
2314                     Binder.restoreCallingIdentity(ident);
2315                 }
2316 
2317                 updateTracker(requestId, frameNumber, requestType, finalResult, isPartialResult);
2318 
2319                 // Fire onCaptureSequenceCompleted
2320                 if (!isPartialResult) {
2321                     checkAndFireSequenceComplete();
2322                 }
2323             }
2324         }
2325 
2326         @Override
onPrepared(int streamId)2327         public void onPrepared(int streamId) {
2328             final OutputConfiguration output;
2329             final StateCallbackKK sessionCallback;
2330 
2331             if (DEBUG) {
2332                 Log.v(TAG, "Stream " + streamId + " is prepared");
2333             }
2334 
2335             synchronized(mInterfaceLock) {
2336                 // Redirect device callback to the offline session in case we are in the middle
2337                 // of an offline switch
2338                 if (mOfflineSessionImpl != null) {
2339                     mOfflineSessionImpl.getCallbacks().onPrepared(streamId);
2340                     return;
2341                 }
2342 
2343                 output = mConfiguredOutputs.get(streamId);
2344                 sessionCallback = mSessionStateCallback;
2345             }
2346 
2347             if (sessionCallback == null) return;
2348 
2349             if (output == null) {
2350                 Log.w(TAG, "onPrepared invoked for unknown output Surface");
2351                 return;
2352             }
2353             final List<Surface> surfaces = output.getSurfaces();
2354             for (Surface surface : surfaces) {
2355                 sessionCallback.onSurfacePrepared(surface);
2356             }
2357         }
2358 
2359         @Override
onRequestQueueEmpty()2360         public void onRequestQueueEmpty() {
2361             final StateCallbackKK sessionCallback;
2362 
2363             if (DEBUG) {
2364                 Log.v(TAG, "Request queue becomes empty");
2365             }
2366 
2367             synchronized(mInterfaceLock) {
2368                 // Redirect device callback to the offline session in case we are in the middle
2369                 // of an offline switch
2370                 if (mOfflineSessionImpl != null) {
2371                     mOfflineSessionImpl.getCallbacks().onRequestQueueEmpty();
2372                     return;
2373                 }
2374 
2375                 sessionCallback = mSessionStateCallback;
2376             }
2377 
2378             if (sessionCallback == null) return;
2379 
2380             sessionCallback.onRequestQueueEmpty();
2381         }
2382 
2383     } // public class CameraDeviceCallbacks
2384 
2385     /**
2386      * A camera specific adapter {@link Executor} that posts all executed tasks onto the given
2387      * {@link Handler}.
2388      *
2389      * @hide
2390      */
2391     private static class CameraHandlerExecutor implements Executor {
2392         private final Handler mHandler;
2393 
CameraHandlerExecutor(@onNull Handler handler)2394         public CameraHandlerExecutor(@NonNull Handler handler) {
2395             mHandler = Objects.requireNonNull(handler);
2396         }
2397 
2398         @Override
execute(Runnable command)2399         public void execute(Runnable command) {
2400             // Return value of 'post()' will be ignored in order to keep the
2401             // same camera behavior. For further details see b/74605221 .
2402             mHandler.post(command);
2403         }
2404     }
2405 
2406     /**
2407      * Default executor management.
2408      *
2409      * <p>
2410      * If executor is null, get the current thread's
2411      * Looper to create a Executor with. If no looper exists, throw
2412      * {@code IllegalArgumentException}.
2413      * </p>
2414      */
checkExecutor(Executor executor)2415     static Executor checkExecutor(Executor executor) {
2416         return (executor == null) ? checkAndWrapHandler(null) : executor;
2417     }
2418 
2419     /**
2420      * Default executor management.
2421      *
2422      * <p>If the callback isn't null, check the executor, otherwise pass it through.</p>
2423      */
checkExecutor(Executor executor, T callback)2424     public static <T> Executor checkExecutor(Executor executor, T callback) {
2425         return (callback != null) ? checkExecutor(executor) : executor;
2426     }
2427 
2428     /**
2429      * Wrap Handler in Executor.
2430      *
2431      * <p>
2432      * If handler is null, get the current thread's
2433      * Looper to create a Executor with. If no looper exists, throw
2434      * {@code IllegalArgumentException}.
2435      * </p>
2436      */
checkAndWrapHandler(Handler handler)2437     public static Executor checkAndWrapHandler(Handler handler) {
2438         return new CameraHandlerExecutor(checkHandler(handler));
2439     }
2440 
2441     /**
2442      * Default handler management.
2443      *
2444      * <p>
2445      * If handler is null, get the current thread's
2446      * Looper to create a Handler with. If no looper exists, throw {@code IllegalArgumentException}.
2447      * </p>
2448      */
checkHandler(Handler handler)2449     static Handler checkHandler(Handler handler) {
2450         if (handler == null) {
2451             Looper looper = Looper.myLooper();
2452             if (looper == null) {
2453                 throw new IllegalArgumentException(
2454                     "No handler given, and current thread has no looper!");
2455             }
2456             handler = new Handler(looper);
2457         }
2458         return handler;
2459     }
2460 
2461     /**
2462      * Default handler management, conditional on there being a callback.
2463      *
2464      * <p>If the callback isn't null, check the handler, otherwise pass it through.</p>
2465      */
checkHandler(Handler handler, T callback)2466     static <T> Handler checkHandler(Handler handler, T callback) {
2467         if (callback != null) {
2468             return checkHandler(handler);
2469         }
2470         return handler;
2471     }
2472 
checkIfCameraClosedOrInError()2473     private void checkIfCameraClosedOrInError() throws CameraAccessException {
2474         if (mRemoteDevice == null) {
2475             throw new IllegalStateException("CameraDevice was already closed");
2476         }
2477         if (mInError) {
2478             throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
2479                     "The camera device has encountered a serious error");
2480         }
2481     }
2482 
2483     /** Whether the camera device has started to close (may not yet have finished) */
isClosed()2484     private boolean isClosed() {
2485         return mClosing.get();
2486     }
2487 
getCharacteristics()2488     private CameraCharacteristics getCharacteristics() {
2489         return mCharacteristics;
2490     }
2491 
2492     /**
2493      * Listener for binder death.
2494      *
2495      * <p> Handle binder death for ICameraDeviceUser. Trigger onError.</p>
2496      */
2497     @Override
binderDied()2498     public void binderDied() {
2499         Log.w(TAG, "CameraDevice " + mCameraId + " died unexpectedly");
2500 
2501         if (mRemoteDevice == null) {
2502             return; // Camera already closed
2503         }
2504 
2505         mInError = true;
2506         Runnable r = new Runnable() {
2507             @Override
2508             public void run() {
2509                 if (!isClosed()) {
2510                     mDeviceCallback.onError(CameraDeviceImpl.this,
2511                             StateCallback.ERROR_CAMERA_SERVICE);
2512                 }
2513             }
2514         };
2515         final long ident = Binder.clearCallingIdentity();
2516         try {
2517             CameraDeviceImpl.this.mDeviceExecutor.execute(r);
2518         } finally {
2519             Binder.restoreCallingIdentity(ident);
2520         }
2521     }
2522 
2523     @Override
setCameraAudioRestriction( @AMERA_AUDIO_RESTRICTION int mode)2524     public void setCameraAudioRestriction(
2525             @CAMERA_AUDIO_RESTRICTION int mode) throws CameraAccessException {
2526         synchronized(mInterfaceLock) {
2527             checkIfCameraClosedOrInError();
2528             mRemoteDevice.setCameraAudioRestriction(mode);
2529         }
2530     }
2531 
2532     @Override
getCameraAudioRestriction()2533     public @CAMERA_AUDIO_RESTRICTION int getCameraAudioRestriction() throws CameraAccessException {
2534         synchronized(mInterfaceLock) {
2535             checkIfCameraClosedOrInError();
2536             return mRemoteDevice.getGlobalAudioRestriction();
2537         }
2538     }
2539 
2540     @Override
createExtensionSession(ExtensionSessionConfiguration extensionConfiguration)2541     public void createExtensionSession(ExtensionSessionConfiguration extensionConfiguration)
2542             throws CameraAccessException {
2543         HashMap<String, CameraCharacteristics> characteristicsMap = new HashMap<>(
2544                 mPhysicalIdsToChars);
2545         characteristicsMap.put(mCameraId, mCharacteristics);
2546         boolean initializationFailed = true;
2547         IBinder token = new Binder(TAG + " : " + mNextSessionId++);
2548         try {
2549             boolean ret = CameraExtensionCharacteristics.registerClient(mContext, token);
2550             if (!ret) {
2551                 token = null;
2552                 throw new UnsupportedOperationException("Unsupported extension!");
2553             }
2554 
2555             if (CameraExtensionCharacteristics.areAdvancedExtensionsSupported()) {
2556                 mCurrentAdvancedExtensionSession =
2557                         CameraAdvancedExtensionSessionImpl.createCameraAdvancedExtensionSession(
2558                                 this, characteristicsMap, mContext, extensionConfiguration,
2559                                 mNextSessionId, token);
2560             } else {
2561                 mCurrentExtensionSession = CameraExtensionSessionImpl.createCameraExtensionSession(
2562                         this, characteristicsMap, mContext, extensionConfiguration,
2563                         mNextSessionId, token);
2564             }
2565             initializationFailed = false;
2566         } catch (RemoteException e) {
2567             throw new CameraAccessException(CameraAccessException.CAMERA_ERROR);
2568         } finally {
2569             if (initializationFailed && (token != null)) {
2570                 CameraExtensionCharacteristics.unregisterClient(mContext, token);
2571             }
2572         }
2573     }
2574 }
2575