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