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