1 /*
2  * Copyright 2020 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 androidx.camera.core.internal;
18 
19 import static androidx.camera.core.CameraEffect.IMAGE_CAPTURE;
20 import static androidx.camera.core.CameraEffect.PREVIEW;
21 import static androidx.camera.core.CameraEffect.VIDEO_CAPTURE;
22 import static androidx.camera.core.DynamicRange.BIT_DEPTH_10_BIT;
23 import static androidx.camera.core.DynamicRange.ENCODING_SDR;
24 import static androidx.camera.core.DynamicRange.ENCODING_UNSPECIFIED;
25 import static androidx.camera.core.ImageCapture.OUTPUT_FORMAT_JPEG_ULTRA_HDR;
26 import static androidx.camera.core.ImageCapture.OUTPUT_FORMAT_RAW;
27 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_OUTPUT_FORMAT;
28 import static androidx.camera.core.impl.StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED;
29 import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAPTURE_TYPE;
30 import static androidx.camera.core.impl.UseCaseConfig.OPTION_TARGET_HIGH_SPEED_FRAME_RATE;
31 import static androidx.camera.core.impl.utils.TransformUtils.rectToSize;
32 import static androidx.camera.core.processing.TargetUtils.getNumberOfTargets;
33 import static androidx.camera.core.streamsharing.StreamSharing.getCaptureTypes;
34 import static androidx.camera.core.streamsharing.StreamSharing.isStreamSharing;
35 import static androidx.core.util.Preconditions.checkArgument;
36 import static androidx.core.util.Preconditions.checkNotNull;
37 import static androidx.core.util.Preconditions.checkState;
38 
39 import static java.util.Collections.emptyList;
40 import static java.util.Objects.requireNonNull;
41 
42 import android.graphics.Matrix;
43 import android.graphics.Rect;
44 import android.graphics.RectF;
45 import android.graphics.SurfaceTexture;
46 import android.util.Log;
47 import android.util.Pair;
48 import android.util.Range;
49 import android.util.Size;
50 import android.view.Surface;
51 
52 import androidx.annotation.GuardedBy;
53 import androidx.annotation.VisibleForTesting;
54 import androidx.camera.core.Camera;
55 import androidx.camera.core.CameraControl;
56 import androidx.camera.core.CameraEffect;
57 import androidx.camera.core.CameraInfo;
58 import androidx.camera.core.CameraSelector;
59 import androidx.camera.core.CompositionSettings;
60 import androidx.camera.core.DynamicRange;
61 import androidx.camera.core.ImageCapture;
62 import androidx.camera.core.Logger;
63 import androidx.camera.core.Preview;
64 import androidx.camera.core.UseCase;
65 import androidx.camera.core.ViewPort;
66 import androidx.camera.core.concurrent.CameraCoordinator;
67 import androidx.camera.core.impl.AdapterCameraInfo;
68 import androidx.camera.core.impl.AdapterCameraInternal;
69 import androidx.camera.core.impl.AttachedSurfaceInfo;
70 import androidx.camera.core.impl.CameraConfig;
71 import androidx.camera.core.impl.CameraConfigs;
72 import androidx.camera.core.impl.CameraControlInternal;
73 import androidx.camera.core.impl.CameraDeviceSurfaceManager;
74 import androidx.camera.core.impl.CameraInfoInternal;
75 import androidx.camera.core.impl.CameraInternal;
76 import androidx.camera.core.impl.CameraMode;
77 import androidx.camera.core.impl.Config;
78 import androidx.camera.core.impl.Identifier;
79 import androidx.camera.core.impl.MutableOptionsBundle;
80 import androidx.camera.core.impl.PreviewConfig;
81 import androidx.camera.core.impl.SessionConfig;
82 import androidx.camera.core.impl.SessionProcessor;
83 import androidx.camera.core.impl.StreamSpec;
84 import androidx.camera.core.impl.SurfaceConfig;
85 import androidx.camera.core.impl.UseCaseConfig;
86 import androidx.camera.core.impl.UseCaseConfigFactory;
87 import androidx.camera.core.impl.UseCaseConfigFactory.CaptureType;
88 import androidx.camera.core.impl.stabilization.StabilizationMode;
89 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
90 import androidx.camera.core.internal.compat.workaround.StreamSharingForceEnabler;
91 import androidx.camera.core.streamsharing.StreamSharing;
92 import androidx.core.util.Preconditions;
93 
94 import com.google.auto.value.AutoValue;
95 
96 import org.jspecify.annotations.NonNull;
97 import org.jspecify.annotations.Nullable;
98 
99 import java.util.ArrayList;
100 import java.util.Arrays;
101 import java.util.Collection;
102 import java.util.Collections;
103 import java.util.HashMap;
104 import java.util.HashSet;
105 import java.util.LinkedHashSet;
106 import java.util.List;
107 import java.util.Map;
108 import java.util.Objects;
109 import java.util.Set;
110 
111 /**
112  * A {@link CameraInternal} adapter which checks that the UseCases to make sure that the resolutions
113  * and image formats can be supported.
114  */
115 public final class CameraUseCaseAdapter implements Camera {
116     private final @NonNull AdapterCameraInternal mCameraInternal;
117     private final @Nullable AdapterCameraInternal mSecondaryCameraInternal;
118     private final CameraDeviceSurfaceManager mCameraDeviceSurfaceManager;
119     private final UseCaseConfigFactory mUseCaseConfigFactory;
120 
121     private static final String TAG = "CameraUseCaseAdapter";
122 
123     private final CameraId mId;
124 
125     // UseCases from the app. This does not include internal UseCases created by CameraX.
126     @GuardedBy("mLock")
127     private final List<UseCase> mAppUseCases = new ArrayList<>();
128     // UseCases sent to the camera including internal UseCases created by CameraX.
129     @GuardedBy("mLock")
130     private final List<UseCase> mCameraUseCases = new ArrayList<>();
131 
132     @GuardedBy("mLock")
133     private final CameraCoordinator mCameraCoordinator;
134 
135     @GuardedBy("mLock")
136     private @Nullable ViewPort mViewPort;
137 
138     @GuardedBy("mLock")
139     private @NonNull List<CameraEffect> mEffects = emptyList();
140 
141     @GuardedBy("mLock")
142     private @NonNull Range<Integer> mTargetHighSpeedFps = FRAME_RATE_RANGE_UNSPECIFIED;
143 
144     // Additional configs to apply onto the UseCases when added to this Camera
145     @GuardedBy("mLock")
146     private final @NonNull CameraConfig mCameraConfig;
147 
148     private final Object mLock = new Object();
149 
150     // This indicates whether or not the UseCases that have been added to this adapter has
151     // actually been attached to the CameraInternal instance.
152     @GuardedBy("mLock")
153     private boolean mAttached = true;
154 
155     // This holds the cached Interop config from CameraControlInternal.
156     @GuardedBy("mLock")
157     private Config mInteropConfig = null;
158 
159     // The placeholder UseCase created to meet combination criteria for Extensions. e.g. When
160     // Extensions require both Preview and ImageCapture and app only provides one of them,
161     // CameraX will create the other and track it with this variable.
162     @GuardedBy("mLock")
163     private @Nullable UseCase mPlaceholderForExtensions;
164     // Current StreamSharing parent UseCase if exists.
165     @GuardedBy("mLock")
166     private @Nullable StreamSharing mStreamSharing;
167 
168     private final @NonNull CompositionSettings mCompositionSettings;
169     private final @NonNull CompositionSettings mSecondaryCompositionSettings;
170     private final StreamSharingForceEnabler mStreamSharingForceEnabler =
171             new StreamSharingForceEnabler();
172 
173     /**
174      * Create a new {@link CameraUseCaseAdapter} instance.
175      *
176      * @param camera                     The camera that is wrapped.
177      * @param cameraCoordinator          Camera coordinator that exposes concurrent camera mode.
178      * @param cameraDeviceSurfaceManager A class that checks for whether a specific camera
179      *                                   can support the set of Surface with set resolutions.
180      * @param useCaseConfigFactory       UseCase config factory that exposes configuration for
181      *                                   each UseCase.
182      */
CameraUseCaseAdapter(@onNull CameraInternal camera, @NonNull CameraCoordinator cameraCoordinator, @NonNull CameraDeviceSurfaceManager cameraDeviceSurfaceManager, @NonNull UseCaseConfigFactory useCaseConfigFactory)183     public CameraUseCaseAdapter(@NonNull CameraInternal camera,
184             @NonNull CameraCoordinator cameraCoordinator,
185             @NonNull CameraDeviceSurfaceManager cameraDeviceSurfaceManager,
186             @NonNull UseCaseConfigFactory useCaseConfigFactory) {
187         this(camera,
188                 null,
189                 new AdapterCameraInfo(camera.getCameraInfoInternal(),
190                         CameraConfigs.defaultConfig()),
191                 null,
192                 CompositionSettings.DEFAULT,
193                 CompositionSettings.DEFAULT,
194                 cameraCoordinator,
195                 cameraDeviceSurfaceManager,
196                 useCaseConfigFactory);
197     }
198 
199     /**
200      * Create a new {@link CameraUseCaseAdapter} instance.
201      *
202      * @param camera                     The camera that is wrapped.
203      * @param secondaryCamera            The secondary camera that is wrapped.
204      * @param adapterCameraInfo       The {@link AdapterCameraInfo} that contains the extra
205      *                                   information to configure the {@link CameraInternal} when
206      *                                   attaching the uses cases of this adapter to the camera.
207      * @param secondaryAdapterCameraInfo The {@link AdapterCameraInfo} of secondary camera.
208      * @param compositionSettings        The composition settings that will be used to configure the
209      *                                   camera.
210      * @param secondaryCompositionSettings  The composition settings that will be used to configure
211      *                                      the secondary camera.
212      * @param cameraCoordinator          Camera coordinator that exposes concurrent camera mode.
213      * @param cameraDeviceSurfaceManager A class that checks for whether a specific camera
214      *                                   can support the set of Surface with set resolutions.
215      * @param useCaseConfigFactory       UseCase config factory that exposes configuration for
216      *                                   each UseCase.
217      */
CameraUseCaseAdapter( @onNull CameraInternal camera, @Nullable CameraInternal secondaryCamera, @NonNull AdapterCameraInfo adapterCameraInfo, @Nullable AdapterCameraInfo secondaryAdapterCameraInfo, @NonNull CompositionSettings compositionSettings, @NonNull CompositionSettings secondaryCompositionSettings, @NonNull CameraCoordinator cameraCoordinator, @NonNull CameraDeviceSurfaceManager cameraDeviceSurfaceManager, @NonNull UseCaseConfigFactory useCaseConfigFactory)218     public CameraUseCaseAdapter(
219             @NonNull CameraInternal camera,
220             @Nullable CameraInternal secondaryCamera,
221             @NonNull AdapterCameraInfo adapterCameraInfo,
222             @Nullable AdapterCameraInfo secondaryAdapterCameraInfo,
223             @NonNull CompositionSettings compositionSettings,
224             @NonNull CompositionSettings secondaryCompositionSettings,
225             @NonNull CameraCoordinator cameraCoordinator,
226             @NonNull CameraDeviceSurfaceManager cameraDeviceSurfaceManager,
227             @NonNull UseCaseConfigFactory useCaseConfigFactory) {
228         mCameraConfig = adapterCameraInfo.getCameraConfig();
229         mCameraInternal = new AdapterCameraInternal(camera, adapterCameraInfo);
230         if (secondaryCamera != null && secondaryAdapterCameraInfo != null) {
231             mSecondaryCameraInternal = new AdapterCameraInternal(secondaryCamera,
232                     secondaryAdapterCameraInfo);
233         } else {
234             mSecondaryCameraInternal = null;
235         }
236         mCompositionSettings = compositionSettings;
237         mSecondaryCompositionSettings = secondaryCompositionSettings;
238         mCameraCoordinator = cameraCoordinator;
239         mCameraDeviceSurfaceManager = cameraDeviceSurfaceManager;
240         mUseCaseConfigFactory = useCaseConfigFactory;
241         mId = generateCameraId(adapterCameraInfo, secondaryAdapterCameraInfo);
242     }
243 
244     /**
245      * Generate a identifier for the {@link AdapterCameraInfo}.
246      */
generateCameraId( @onNull AdapterCameraInfo primaryCameraInfo, @Nullable AdapterCameraInfo secondaryCameraInfo)247     public static @NonNull CameraId generateCameraId(
248             @NonNull AdapterCameraInfo primaryCameraInfo,
249             @Nullable AdapterCameraInfo secondaryCameraInfo) {
250         return CameraId.create(
251                 primaryCameraInfo.getCameraId()
252                         + (secondaryCameraInfo == null ? "" : secondaryCameraInfo.getCameraId()),
253                 primaryCameraInfo.getCameraConfig().getCompatibilityId());
254     }
255 
256     /**
257      * Returns the identifier for this {@link CameraUseCaseAdapter}.
258      */
getCameraId()259     public @NonNull CameraId getCameraId() {
260         return mId;
261     }
262 
263     /**
264      * Returns true if the {@link CameraUseCaseAdapter} is an equivalent camera.
265      */
isEquivalent(@onNull CameraUseCaseAdapter cameraUseCaseAdapter)266     public boolean isEquivalent(@NonNull CameraUseCaseAdapter cameraUseCaseAdapter) {
267         return getCameraId().equals(cameraUseCaseAdapter.getCameraId());
268     }
269 
270     /**
271      * Set the viewport that will be used for the {@link UseCase} attached to the camera.
272      */
setViewPort(@ullable ViewPort viewPort)273     public void setViewPort(@Nullable ViewPort viewPort) {
274         synchronized (mLock) {
275             mViewPort = viewPort;
276         }
277     }
278 
279     /**
280      * Set the effects that will be used for the {@link UseCase} attached to the camera.
281      */
setEffects(@ullable List<CameraEffect> effects)282     public void setEffects(@Nullable List<CameraEffect> effects) {
283         synchronized (mLock) {
284             mEffects = effects;
285         }
286     }
287 
288     /**
289      * Set the target high speed frame rate that will be used for the {@link UseCase} attached to
290      * the camera.
291      */
setTargetHighSpeedFrameRate(@onNull Range<Integer> frameRate)292     public void setTargetHighSpeedFrameRate(@NonNull Range<Integer> frameRate) {
293         synchronized (mLock) {
294             mTargetHighSpeedFps = frameRate;
295         }
296     }
297 
298     /**
299      * Add the specified collection of {@link UseCase} to the adapter with dual camera support.
300      *
301      * @throws CameraException Thrown if the combination of newly added UseCases and the
302      *                         currently added UseCases exceed the capability of the camera.
303      */
addUseCases(@onNull Collection<UseCase> appUseCasesToAdd)304     public void addUseCases(@NonNull Collection<UseCase> appUseCasesToAdd) throws CameraException {
305         synchronized (mLock) {
306             // Configure the CameraConfig when binding
307             mCameraInternal.setExtendedConfig(mCameraConfig);
308             if (mSecondaryCameraInternal != null) {
309                 mSecondaryCameraInternal.setExtendedConfig(mCameraConfig);
310             }
311             Set<UseCase> appUseCases = new LinkedHashSet<>(mAppUseCases);
312             //TODO(b/266641900): must be LinkedHashSet otherwise ExistingActivityLifecycleTest
313             // fails due to a camera-pipe integration bug.
314             appUseCases.addAll(appUseCasesToAdd);
315             try {
316                 updateUseCases(appUseCases,
317                         mSecondaryCameraInternal != null, mSecondaryCameraInternal != null);
318             } catch (IllegalArgumentException e) {
319                 throw new CameraException(e);
320             }
321         }
322     }
323 
324     /**
325      * Remove the specified collection of {@link UseCase} from the adapter.
326      */
removeUseCases(@onNull Collection<UseCase> useCasesToRemove)327     public void removeUseCases(@NonNull Collection<UseCase> useCasesToRemove) {
328         synchronized (mLock) {
329             Set<UseCase> appUseCases = new LinkedHashSet<>(mAppUseCases);
330             appUseCases.removeAll(useCasesToRemove);
331             updateUseCases(appUseCases,
332                     mSecondaryCameraInternal != null, mSecondaryCameraInternal != null);
333         }
334     }
335 
336     /**
337      * Updates the states based the new app UseCases.
338      */
updateUseCases(@onNull Collection<UseCase> appUseCases)339     void updateUseCases(@NonNull Collection<UseCase> appUseCases) {
340         updateUseCases(appUseCases, /*applyStreamSharing*/false, /*isDualCamera*/false);
341     }
342 
343     /**
344      * Updates the states based the new app UseCases.
345      *
346      * <p> This method calculates the new camera UseCases based on the input and the current state,
347      * attach/detach the camera UseCases, and save the updated state in following member variables:
348      * {@link #mCameraUseCases}, {@link #mAppUseCases} and {@link #mPlaceholderForExtensions}.
349      *
350      * @throws IllegalArgumentException if the UseCase combination is not supported. In that case,
351      *                                  it will not update the internal states.
352      */
updateUseCases(@onNull Collection<UseCase> appUseCases, boolean applyStreamSharing, boolean isDualCamera)353     void updateUseCases(@NonNull Collection<UseCase> appUseCases,
354             boolean applyStreamSharing,
355             boolean isDualCamera) {
356         synchronized (mLock) {
357             checkUnsupportedFeatureCombinationAndThrow(appUseCases);
358 
359             // Force enable StreamSharing for Extensions to support VideoCapture. This means that
360             // applyStreamSharing is set to true when the use case combination contains
361             // VideoCapture and Extensions is enabled.
362             if (!applyStreamSharing && shouldForceEnableStreamSharing(appUseCases)) {
363                 updateUseCases(appUseCases, /*applyStreamSharing*/true, isDualCamera);
364                 return;
365             }
366 
367             // Calculate camera UseCases and keep the result in local variables in case they don't
368             // meet the stream combination rules.
369             StreamSharing streamSharing = createOrReuseStreamSharing(appUseCases,
370                     applyStreamSharing);
371             UseCase placeholderForExtensions = calculatePlaceholderForExtensions(appUseCases,
372                     streamSharing);
373             Collection<UseCase> cameraUseCases =
374                     calculateCameraUseCases(appUseCases, placeholderForExtensions, streamSharing);
375 
376             // Calculate the action items.
377             List<UseCase> cameraUseCasesToAttach = new ArrayList<>(cameraUseCases);
378             cameraUseCasesToAttach.removeAll(mCameraUseCases);
379             List<UseCase> cameraUseCasesToKeep = new ArrayList<>(cameraUseCases);
380             cameraUseCasesToKeep.retainAll(mCameraUseCases);
381             List<UseCase> cameraUseCasesToDetach = new ArrayList<>(mCameraUseCases);
382             cameraUseCasesToDetach.removeAll(cameraUseCases);
383 
384             // Calculate suggested resolutions. This step throws exception if the camera UseCases
385             // fails the supported stream combination rules.
386             Map<UseCase, ConfigPair> configs = getConfigs(cameraUseCasesToAttach,
387                     mCameraConfig.getUseCaseConfigFactory(), mUseCaseConfigFactory,
388                     mTargetHighSpeedFps);
389             Map<UseCase, StreamSpec> primaryStreamSpecMap;
390             Map<UseCase, StreamSpec> secondaryStreamSpecMap = Collections.emptyMap();
391             try {
392                 primaryStreamSpecMap = calculateSuggestedStreamSpecs(
393                         getCameraMode(),
394                         mCameraInternal.getCameraInfoInternal(), cameraUseCasesToAttach,
395                         cameraUseCasesToKeep, configs);
396                 if (mSecondaryCameraInternal != null) {
397                     secondaryStreamSpecMap = calculateSuggestedStreamSpecs(
398                             getCameraMode(),
399                             requireNonNull(mSecondaryCameraInternal).getCameraInfoInternal(),
400                             cameraUseCasesToAttach,
401                             cameraUseCasesToKeep, configs);
402                 }
403                 // TODO(b/265704882): enable stream sharing for LEVEL_3 and high preview
404                 //  resolution. Throw exception here if (applyStreamSharing == false), both video
405                 //  and preview are used and preview resolution is lower than user configuration.
406             } catch (IllegalArgumentException exception) {
407                 // TODO(b/270187871): instead of catch and retry, we can check UseCase
408                 //  combination directly with #isUseCasesCombinationSupported(). However
409                 //  calculateSuggestedStreamSpecs() is currently slow. We will do it after it's
410                 //  optimized
411                 // Only allow StreamSharing for non-concurrent mode.
412                 if (!applyStreamSharing && !hasExtension()
413                         && mCameraCoordinator.getCameraOperatingMode()
414                         != CameraCoordinator.CAMERA_OPERATING_MODE_CONCURRENT) {
415                     // Try again and see if StreamSharing resolves the issue.
416                     updateUseCases(appUseCases, /*applyStreamSharing*/true, isDualCamera);
417                     return;
418                 } else {
419                     // If StreamSharing already on or not enabled, throw exception.
420                     throw exception;
421                 }
422             }
423 
424             // Update properties.
425             updateViewPortAndSensorToBufferMatrix(primaryStreamSpecMap, cameraUseCases);
426             updateEffects(mEffects, cameraUseCases, appUseCases);
427 
428             // Detach unused UseCases.
429             for (UseCase useCase : cameraUseCasesToDetach) {
430                 useCase.unbindFromCamera(mCameraInternal);
431             }
432             mCameraInternal.detachUseCases(cameraUseCasesToDetach);
433 
434             // Detach unused UseCases for secondary camera.
435             if (mSecondaryCameraInternal != null) {
436                 for (UseCase useCase : cameraUseCasesToDetach) {
437                     useCase.unbindFromCamera(requireNonNull(mSecondaryCameraInternal));
438                 }
439                 requireNonNull(mSecondaryCameraInternal)
440                         .detachUseCases(cameraUseCasesToDetach);
441             }
442 
443             // Update StreamSpec for UseCases to keep.
444             if (cameraUseCasesToDetach.isEmpty()) {
445                 // Only do this if we are not removing UseCase, because updating SessionConfig
446                 // when removing UseCases may lead to flickering.
447                 for (UseCase useCase : cameraUseCasesToKeep) {
448                     // Assume secondary camera will not have implementation options in dual camera.
449                     if (primaryStreamSpecMap.containsKey(useCase)) {
450                         StreamSpec newStreamSpec = primaryStreamSpecMap.get(useCase);
451                         Config config = newStreamSpec.getImplementationOptions();
452                         if (config != null && hasImplementationOptionChanged(newStreamSpec,
453                                 useCase.getSessionConfig())) {
454                             useCase.updateSuggestedStreamSpecImplementationOptions(config);
455                             if (mAttached) {
456                                 mCameraInternal.onUseCaseUpdated(useCase);
457                                 if (mSecondaryCameraInternal != null) {
458                                     requireNonNull(mSecondaryCameraInternal)
459                                             .onUseCaseUpdated(useCase);
460                                 }
461                             }
462                         }
463                     }
464                 }
465             }
466 
467             // Attach new UseCases.
468             for (UseCase useCase : cameraUseCasesToAttach) {
469                 ConfigPair configPair = requireNonNull(configs.get(useCase));
470                 if (mSecondaryCameraInternal != null) {
471                     useCase.bindToCamera(mCameraInternal,
472                             requireNonNull(mSecondaryCameraInternal),
473                             configPair.mExtendedConfig,
474                             configPair.mCameraConfig);
475                     useCase.updateSuggestedStreamSpec(
476                             Preconditions.checkNotNull(primaryStreamSpecMap.get(useCase)),
477                             secondaryStreamSpecMap.get(useCase));
478                 } else {
479                     useCase.bindToCamera(mCameraInternal,
480                             null,
481                             configPair.mExtendedConfig,
482                             configPair.mCameraConfig);
483                     useCase.updateSuggestedStreamSpec(
484                             Preconditions.checkNotNull(primaryStreamSpecMap.get(useCase)),
485                             null);
486                 }
487             }
488             if (mAttached) {
489                 mCameraInternal.attachUseCases(cameraUseCasesToAttach);
490                 if (mSecondaryCameraInternal != null) {
491                     requireNonNull(mSecondaryCameraInternal)
492                             .attachUseCases(cameraUseCasesToAttach);
493                 }
494             }
495 
496             // Once UseCases are detached/attached, notify the camera.
497             for (UseCase useCase : cameraUseCasesToAttach) {
498                 useCase.notifyState();
499             }
500 
501             // The changes are successful. Update the states of this class.
502             mAppUseCases.clear();
503             mAppUseCases.addAll(appUseCases);
504             mCameraUseCases.clear();
505             mCameraUseCases.addAll(cameraUseCases);
506             mPlaceholderForExtensions = placeholderForExtensions;
507             mStreamSharing = streamSharing;
508         }
509     }
510 
shouldForceEnableStreamSharing(@onNull Collection<UseCase> appUseCases)511     private boolean shouldForceEnableStreamSharing(@NonNull Collection<UseCase> appUseCases) {
512         if (hasExtension() && hasVideoCapture(appUseCases)) {
513             return true;
514         }
515 
516         return mStreamSharingForceEnabler.shouldForceEnableStreamSharing(
517                 mCameraInternal.getCameraInfoInternal().getCameraId(), appUseCases);
518     }
519 
520     /**
521      * Return true if the given StreamSpec has any option with a different value than that
522      * of the given sessionConfig.
523      */
hasImplementationOptionChanged( StreamSpec streamSpec, SessionConfig sessionConfig)524     private static boolean hasImplementationOptionChanged(
525             StreamSpec streamSpec,
526             SessionConfig sessionConfig) {
527         Config newStreamSpecOptions = streamSpec.getImplementationOptions();
528         Config sessionConfigOptions = sessionConfig.getImplementationOptions();
529         if (newStreamSpecOptions.listOptions().size()
530                 != sessionConfig.getImplementationOptions().listOptions().size()) {
531             return true;
532         }
533         for (Config.Option<?> newOption : newStreamSpecOptions.listOptions()) {
534             if (!sessionConfigOptions.containsOption(newOption)
535                     || !Objects.equals(sessionConfigOptions.retrieveOption(newOption),
536                     newStreamSpecOptions.retrieveOption(newOption))) {
537                 return true;
538             }
539         }
540         return false;
541     }
542 
getCameraMode()543     private @CameraMode.Mode int getCameraMode() {
544         synchronized (mLock) {
545             if (mCameraCoordinator.getCameraOperatingMode()
546                     == CameraCoordinator.CAMERA_OPERATING_MODE_CONCURRENT) {
547                 return CameraMode.CONCURRENT_CAMERA;
548             }
549         }
550 
551         // TODO(b/271199876): return ULTRA_HIGH_RESOLUTION_CAMERA when it can be enabled via
552         //  Camera2Interop
553 
554         return CameraMode.DEFAULT;
555     }
556 
hasExtension()557     private boolean hasExtension() {
558         synchronized (mLock) {
559             return mCameraConfig.getSessionProcessor(null) != null;
560         }
561     }
562 
563     /**
564      * Returns {@link UseCase}s qualified for {@link StreamSharing}.
565      */
getStreamSharingChildren(@onNull Collection<UseCase> appUseCases, boolean forceSharingToPreviewAndVideo)566     private @NonNull Set<UseCase> getStreamSharingChildren(@NonNull Collection<UseCase> appUseCases,
567             boolean forceSharingToPreviewAndVideo) {
568         Set<UseCase> children = new HashSet<>();
569         int sharingTargets = getSharingTargets(forceSharingToPreviewAndVideo);
570         for (UseCase useCase : appUseCases) {
571             checkArgument(!isStreamSharing(useCase), "Only support one level of sharing for now.");
572             if (useCase.isEffectTargetsSupported(sharingTargets)) {
573                 children.add(useCase);
574             }
575         }
576         return children;
577     }
578 
579     @CameraEffect.Targets
getSharingTargets(boolean forceSharingToPreviewAndVideo)580     private int getSharingTargets(boolean forceSharingToPreviewAndVideo) {
581         synchronized (mLock) {
582             // Find the only effect that has more than one targets.
583             CameraEffect sharingEffect = null;
584             for (CameraEffect effect : mEffects) {
585                 if (getNumberOfTargets(effect.getTargets()) > 1) {
586                     checkState(sharingEffect == null, "Can only have one sharing effect.");
587                     sharingEffect = effect;
588                 }
589             }
590             int sharingTargets = sharingEffect == null ? 0 : sharingEffect.getTargets();
591 
592             // Share stream to preview and video capture if the device requires it.
593             if (forceSharingToPreviewAndVideo) {
594                 sharingTargets |= PREVIEW | VIDEO_CAPTURE;
595             }
596             return sharingTargets;
597         }
598     }
599 
600     /**
601      * Creates a new {@link StreamSharing} or returns the existing one.
602      *
603      * <p>Returns the existing {@link StreamSharing} if the children have not changed.
604      * Otherwise, create a new {@link StreamSharing} and return.
605      *
606      * <p>Returns null when there is no need to share the stream, or the combination of children
607      * UseCase are invalid(e.g. contains more than 1 UseCase per type).
608      */
createOrReuseStreamSharing( @onNull Collection<UseCase> appUseCases, boolean forceSharingToPreviewAndVideo)609     private @Nullable StreamSharing createOrReuseStreamSharing(
610             @NonNull Collection<UseCase> appUseCases, boolean forceSharingToPreviewAndVideo) {
611         synchronized (mLock) {
612             Set<UseCase> newChildren = getStreamSharingChildren(appUseCases,
613                     forceSharingToPreviewAndVideo);
614             if (newChildren.size() < 2) {
615                 // No need to share the stream for 1 or less children. Except the case that
616                 // StreamSharing is enabled for Extensions to support VideoCapture.
617                 if (!(hasExtension() && hasVideoCapture(newChildren))) {
618                     return null;
619                 }
620             }
621             if (mStreamSharing != null && mStreamSharing.getChildren().equals(newChildren)) {
622                 // Returns the current instance if the new children equals the old.
623                 return requireNonNull(mStreamSharing);
624             }
625 
626             if (!isStreamSharingChildrenCombinationValid(newChildren)) {
627                 return null;
628             }
629 
630             return new StreamSharing(mCameraInternal,
631                     mSecondaryCameraInternal,
632                     mCompositionSettings,
633                     mSecondaryCompositionSettings,
634                     newChildren,
635                     mUseCaseConfigFactory);
636         }
637     }
638 
639     /**
640      * Returns true if the children are valid for {@link StreamSharing}.
641      */
isStreamSharingChildrenCombinationValid( @onNull Collection<UseCase> children)642     static boolean isStreamSharingChildrenCombinationValid(
643             @NonNull Collection<UseCase> children) {
644         int[] validChildrenTypes = {PREVIEW, VIDEO_CAPTURE, IMAGE_CAPTURE};
645         Set<Integer> childrenTypes = new HashSet<>();
646         // Loop through all children and add supported types.
647         for (UseCase child : children) {
648             for (int type : validChildrenTypes) {
649                 if (child.isEffectTargetsSupported(type)) {
650                     if (childrenTypes.contains(type)) {
651                         // Return false if there are 2 use case supporting the same type.
652                         return false;
653                     }
654                     childrenTypes.add(type);
655                 }
656             }
657         }
658         return true;
659     }
660 
661     /**
662      * Returns {@link UseCase} that connects to the camera.
663      */
calculateCameraUseCases(@onNull Collection<UseCase> appUseCases, @Nullable UseCase placeholderForExtensions, @Nullable StreamSharing streamSharing)664     static Collection<UseCase> calculateCameraUseCases(@NonNull Collection<UseCase> appUseCases,
665             @Nullable UseCase placeholderForExtensions,
666             @Nullable StreamSharing streamSharing) {
667         List<UseCase> useCases = new ArrayList<>(appUseCases);
668         if (placeholderForExtensions != null) {
669             useCases.add(placeholderForExtensions);
670         }
671         if (streamSharing != null) {
672             useCases.add(streamSharing);
673             useCases.removeAll(streamSharing.getChildren());
674         }
675         return useCases;
676     }
677 
678     /**
679      * Returns the UseCases currently associated with the adapter.
680      *
681      * <p> The UseCases may or may not be actually attached to the underlying
682      * {@link CameraInternal} instance.
683      */
getUseCases()684     public @NonNull List<UseCase> getUseCases() {
685         synchronized (mLock) {
686             return new ArrayList<>(mAppUseCases);
687         }
688     }
689 
690     @VisibleForTesting
getCameraUseCases()691     public @NonNull Collection<UseCase> getCameraUseCases() {
692         synchronized (mLock) {
693             return new ArrayList<>(mCameraUseCases);
694         }
695     }
696 
697     /**
698      * Attach the UseCases to the {@link CameraInternal} camera so that the UseCases can receive
699      * data if they are active.
700      *
701      * <p> This will start the underlying {@link CameraInternal} instance.
702      *
703      * <p> This will restore the cached Interop config to the {@link CameraInternal}.
704      */
attachUseCases()705     public void attachUseCases() {
706         synchronized (mLock) {
707             if (!mAttached) {
708                 // Ensure the current opening camera has the right camera config.
709                 if (!mCameraUseCases.isEmpty()) {
710                     mCameraInternal.setExtendedConfig(mCameraConfig);
711                     if (mSecondaryCameraInternal != null) {
712                         mSecondaryCameraInternal.setExtendedConfig(mCameraConfig);
713                     }
714                 }
715                 mCameraInternal.attachUseCases(mCameraUseCases);
716                 if (mSecondaryCameraInternal != null) {
717                     mSecondaryCameraInternal.attachUseCases(mCameraUseCases);
718                 }
719                 restoreInteropConfig();
720 
721                 // Notify to update the use case's active state because it may be cleared if the
722                 // use case was ever detached from a camera previously.
723                 for (UseCase useCase : mCameraUseCases) {
724                     useCase.notifyState();
725                 }
726 
727                 mAttached = true;
728             }
729         }
730     }
731 
732     /**
733      * When in active resuming mode, it will actively retry opening the camera periodically to
734      * resume regardless of the camera availability if the camera is interrupted in
735      * OPEN/OPENING/PENDING_OPEN state.
736      *
737      * When not in actively resuming mode, it will retry opening camera only when camera
738      * becomes available.
739      */
setActiveResumingMode(boolean enabled)740     public void setActiveResumingMode(boolean enabled) {
741         mCameraInternal.setActiveResumingMode(enabled);
742     }
743 
744     /**
745      * Detach the UseCases from the {@link CameraInternal} so that the UseCases stop receiving data.
746      *
747      * <p> This will stop the underlying {@link CameraInternal} instance.
748      *
749      * <p> This will cache the Interop config from the {@link CameraInternal}.
750      */
detachUseCases()751     public void detachUseCases() {
752         synchronized (mLock) {
753             if (mAttached) {
754                 mCameraInternal.detachUseCases(new ArrayList<>(mCameraUseCases));
755                 if (mSecondaryCameraInternal != null) {
756                     mSecondaryCameraInternal.detachUseCases(new ArrayList<>(mCameraUseCases));
757                 }
758                 cacheInteropConfig();
759                 mAttached = false;
760             }
761         }
762     }
763 
764     /**
765      * Restores the cached InteropConfig to the camera.
766      */
restoreInteropConfig()767     private void restoreInteropConfig() {
768         synchronized (mLock) {
769             if (mInteropConfig != null) {
770                 mCameraInternal.getCameraControlInternal().addInteropConfig(mInteropConfig);
771             }
772         }
773     }
774 
775     /**
776      * Caches and clears the InteropConfig from the camera.
777      */
cacheInteropConfig()778     private void cacheInteropConfig() {
779         synchronized (mLock) {
780             CameraControlInternal cameraControlInternal =
781                     mCameraInternal.getCameraControlInternal();
782             mInteropConfig = cameraControlInternal.getInteropConfig();
783             cameraControlInternal.clearInteropConfig();
784         }
785     }
786 
calculateSuggestedStreamSpecs( @ameraMode.Mode int cameraMode, @NonNull CameraInfoInternal cameraInfoInternal, @NonNull Collection<UseCase> newUseCases, @NonNull Collection<UseCase> currentUseCases, @NonNull Map<UseCase, ConfigPair> configPairMap)787     private Map<UseCase, StreamSpec> calculateSuggestedStreamSpecs(
788             @CameraMode.Mode int cameraMode,
789             @NonNull CameraInfoInternal cameraInfoInternal,
790             @NonNull Collection<UseCase> newUseCases,
791             @NonNull Collection<UseCase> currentUseCases,
792             @NonNull Map<UseCase, ConfigPair> configPairMap) {
793         List<AttachedSurfaceInfo> existingSurfaces = new ArrayList<>();
794         String cameraId = cameraInfoInternal.getCameraId();
795         Map<UseCase, StreamSpec> suggestedStreamSpecs = new HashMap<>();
796         Map<AttachedSurfaceInfo, UseCase> surfaceInfoUseCaseMap = new HashMap<>();
797 
798         // Get resolution for current use cases.
799         for (UseCase useCase : currentUseCases) {
800             SurfaceConfig surfaceConfig =
801                     mCameraDeviceSurfaceManager.transformSurfaceConfig(
802                             cameraMode,
803                             cameraId,
804                             useCase.getImageFormat(),
805                             useCase.getAttachedSurfaceResolution());
806             AttachedSurfaceInfo attachedSurfaceInfo = AttachedSurfaceInfo.create(surfaceConfig,
807                     useCase.getImageFormat(), useCase.getAttachedSurfaceResolution(),
808                     Preconditions.checkNotNull(useCase.getAttachedStreamSpec()).getDynamicRange(),
809                     getCaptureTypes(useCase),
810                     useCase.getAttachedStreamSpec().getImplementationOptions(),
811                     useCase.getCurrentConfig().getTargetFrameRate(null),
812                     Preconditions.checkNotNull(
813                             useCase.getCurrentConfig().getTargetHighSpeedFrameRate(
814                                     FRAME_RATE_RANGE_UNSPECIFIED)));
815             existingSurfaces.add(attachedSurfaceInfo);
816             surfaceInfoUseCaseMap.put(attachedSurfaceInfo, useCase);
817             suggestedStreamSpecs.put(useCase, useCase.getAttachedStreamSpec());
818         }
819 
820         // Calculate resolution for new use cases.
821         if (!newUseCases.isEmpty()) {
822             Map<UseCaseConfig<?>, UseCase> configToUseCaseMap = new HashMap<>();
823             Map<UseCaseConfig<?>, List<Size>> configToSupportedSizesMap = new HashMap<>();
824             Rect sensorRect;
825             try {
826                 sensorRect = mCameraInternal.getCameraControlInternal().getSensorRect();
827             } catch (NullPointerException e) {
828                 // TODO(b/274531208): Remove the unnecessary SENSOR_INFO_ACTIVE_ARRAY_SIZE NPE
829                 //  check related code only which is used for robolectric tests
830                 sensorRect = null;
831             }
832             SupportedOutputSizesSorter supportedOutputSizesSorter = new SupportedOutputSizesSorter(
833                     cameraInfoInternal,
834                     sensorRect != null ? rectToSize(sensorRect) : null);
835             boolean isPreviewStabilizationOn = false;
836             for (UseCase useCase : newUseCases) {
837                 ConfigPair configPair = configPairMap.get(useCase);
838                 // Combine with default configuration.
839                 UseCaseConfig<?> combinedUseCaseConfig =
840                         useCase.mergeConfigs(cameraInfoInternal, configPair.mExtendedConfig,
841                                 configPair.mCameraConfig);
842                 configToUseCaseMap.put(combinedUseCaseConfig, useCase);
843                 configToSupportedSizesMap.put(combinedUseCaseConfig,
844                         supportedOutputSizesSorter.getSortedSupportedOutputSizes(
845                                 combinedUseCaseConfig));
846 
847                 if (useCase.getCurrentConfig() instanceof PreviewConfig) {
848                     isPreviewStabilizationOn =
849                             ((PreviewConfig) useCase.getCurrentConfig())
850                                     .getPreviewStabilizationMode() == StabilizationMode.ON;
851                 }
852             }
853 
854             // Get suggested stream specifications and update the use case session configuration
855             Pair<Map<UseCaseConfig<?>, StreamSpec>, Map<AttachedSurfaceInfo, StreamSpec>>
856                     streamSpecMaps =
857                     mCameraDeviceSurfaceManager.getSuggestedStreamSpecs(
858                             cameraMode,
859                             cameraId, existingSurfaces,
860                             configToSupportedSizesMap,
861                             isPreviewStabilizationOn,
862                             hasVideoCapture(newUseCases));
863 
864             for (Map.Entry<UseCaseConfig<?>, UseCase> entry : configToUseCaseMap.entrySet()) {
865                 suggestedStreamSpecs.put(entry.getValue(),
866                         streamSpecMaps.first.get(entry.getKey()));
867             }
868             for (Map.Entry<AttachedSurfaceInfo, StreamSpec> entry :
869                     streamSpecMaps.second.entrySet()) {
870                 if (surfaceInfoUseCaseMap.containsKey(entry.getKey())) {
871                     suggestedStreamSpecs.put(surfaceInfoUseCaseMap.get(entry.getKey()),
872                             entry.getValue());
873                 }
874             }
875         }
876         return suggestedStreamSpecs;
877     }
878 
879     @VisibleForTesting
updateEffects(@onNull List<CameraEffect> effects, @NonNull Collection<UseCase> cameraUseCases, @NonNull Collection<UseCase> appUseCases)880     static void updateEffects(@NonNull List<CameraEffect> effects,
881             @NonNull Collection<UseCase> cameraUseCases,
882             @NonNull Collection<UseCase> appUseCases) {
883         // Match camera UseCases first. Apply the effect early in the pipeline if possible.
884         List<CameraEffect> unusedEffects = setEffectsOnUseCases(effects, cameraUseCases);
885 
886         // Match unused effects with app only UseCases.
887         List<UseCase> appOnlyUseCases = new ArrayList<>(appUseCases);
888         appOnlyUseCases.removeAll(cameraUseCases);
889         unusedEffects = setEffectsOnUseCases(unusedEffects, appOnlyUseCases);
890 
891         if (unusedEffects.size() > 0) {
892             Logger.w(TAG, "Unused effects: " + unusedEffects);
893         }
894     }
895 
896     /**
897      * Sets effects on the given {@link UseCase} list and returns unused effects.
898      */
setEffectsOnUseCases( @onNull List<CameraEffect> effects, @NonNull Collection<UseCase> useCases)899     private static @NonNull List<CameraEffect> setEffectsOnUseCases(
900             @NonNull List<CameraEffect> effects, @NonNull Collection<UseCase> useCases) {
901         List<CameraEffect> unusedEffects = new ArrayList<>(effects);
902         for (UseCase useCase : useCases) {
903             useCase.setEffect(null);
904             for (CameraEffect effect : effects) {
905                 if (useCase.isEffectTargetsSupported(effect.getTargets())) {
906                     checkState(useCase.getEffect() == null,
907                             useCase + " already has effect" + useCase.getEffect());
908                     useCase.setEffect(effect);
909                     unusedEffects.remove(effect);
910                 }
911             }
912         }
913         return unusedEffects;
914     }
915 
updateViewPortAndSensorToBufferMatrix( @onNull Map<UseCase, StreamSpec> suggestedStreamSpecMap, @NonNull Collection<UseCase> useCases)916     private void updateViewPortAndSensorToBufferMatrix(
917             @NonNull Map<UseCase, StreamSpec> suggestedStreamSpecMap,
918             @NonNull Collection<UseCase> useCases) {
919         synchronized (mLock) {
920             if (mViewPort != null && !useCases.isEmpty()) {
921                 Integer lensFacing = mCameraInternal.getCameraInfoInternal().getLensFacing();
922                 boolean isFrontCamera;
923                 if (lensFacing == null) {
924                     // TODO(b/122975195): If the lens facing is null, it's probably an external
925                     //  camera. We treat it as like a front camera with unverified behaviors. Will
926                     //  have to define this later.
927                     Logger.w(TAG, "The lens facing is null, probably an external.");
928                     isFrontCamera = true;
929                 } else {
930                     isFrontCamera = lensFacing == CameraSelector.LENS_FACING_FRONT;
931                 }
932                 // Calculate crop rect if view port is provided.
933                 Map<UseCase, Rect> cropRectMap = ViewPorts.calculateViewPortRects(
934                         mCameraInternal.getCameraControlInternal().getSensorRect(),
935                         isFrontCamera,
936                         mViewPort.getAspectRatio(),
937                         mCameraInternal.getCameraInfoInternal().getSensorRotationDegrees(
938                                 mViewPort.getRotation()),
939                         mViewPort.getScaleType(),
940                         mViewPort.getLayoutDirection(),
941                         suggestedStreamSpecMap);
942                 for (UseCase useCase : useCases) {
943                     useCase.setViewPortCropRect(
944                             Preconditions.checkNotNull(cropRectMap.get(useCase)));
945                 }
946             }
947 
948             // Regardless of having ViewPort, SensorToBufferTransformMatrix must be set correctly
949             // in order for get the correct meteringPoint coordinates.
950             for (UseCase useCase : useCases) {
951                 useCase.setSensorToBufferTransformMatrix(
952                         calculateSensorToBufferTransformMatrix(
953                                 mCameraInternal.getCameraControlInternal().getSensorRect(),
954                                 Preconditions.checkNotNull(
955                                         suggestedStreamSpecMap.get(useCase)).getResolution()));
956             }
957         }
958     }
959 
calculateSensorToBufferTransformMatrix( @onNull Rect fullSensorRect, @NonNull Size useCaseSize)960     private static @NonNull Matrix calculateSensorToBufferTransformMatrix(
961             @NonNull Rect fullSensorRect,
962             @NonNull Size useCaseSize) {
963         checkArgument(
964                 fullSensorRect.width() > 0 && fullSensorRect.height() > 0,
965                 "Cannot compute viewport crop rects zero sized sensor rect.");
966         RectF fullSensorRectF = new RectF(fullSensorRect);
967         Matrix sensorToUseCaseTransformation = new Matrix();
968         RectF srcRect = new RectF(0, 0, useCaseSize.getWidth(),
969                 useCaseSize.getHeight());
970         sensorToUseCaseTransformation.setRectToRect(srcRect, fullSensorRectF,
971                 Matrix.ScaleToFit.CENTER);
972         sensorToUseCaseTransformation.invert(sensorToUseCaseTransformation);
973         return sensorToUseCaseTransformation;
974     }
975 
976     // Pair of UseCase configs. One for the extended config applied on top of the use case and
977     // the camera default which applied underneath the use case's config.
978     private static class ConfigPair {
ConfigPair(UseCaseConfig<?> extendedConfig, UseCaseConfig<?> cameraConfig)979         ConfigPair(UseCaseConfig<?> extendedConfig, UseCaseConfig<?> cameraConfig) {
980             mExtendedConfig = extendedConfig;
981             mCameraConfig = cameraConfig;
982         }
983 
984         UseCaseConfig<?> mExtendedConfig;
985         UseCaseConfig<?> mCameraConfig;
986     }
987 
988     /**
989      * Gets a map of the configs for the use cases from the respective factories.
990      */
getConfigs(@onNull Collection<UseCase> useCases, @NonNull UseCaseConfigFactory extendedFactory, @NonNull UseCaseConfigFactory cameraFactory, @NonNull Range<Integer> targetHighSpeedFps)991     private static Map<UseCase, ConfigPair> getConfigs(@NonNull Collection<UseCase> useCases,
992             @NonNull UseCaseConfigFactory extendedFactory,
993             @NonNull UseCaseConfigFactory cameraFactory,
994             @NonNull Range<Integer> targetHighSpeedFps) {
995         Map<UseCase, ConfigPair> configs = new HashMap<>();
996         for (UseCase useCase : useCases) {
997             UseCaseConfig<?> extendedConfig;
998             if (isStreamSharing(useCase)) {
999                 extendedConfig = generateExtendedStreamSharingConfigFromPreview(extendedFactory,
1000                         (StreamSharing) useCase);
1001             } else {
1002                 extendedConfig = useCase.getDefaultConfig(false, extendedFactory);
1003             }
1004             UseCaseConfig<?> cameraConfig = useCase.getDefaultConfig(true, cameraFactory);
1005             cameraConfig = attachUseCaseSharedConfigs(useCase, cameraConfig, targetHighSpeedFps);
1006             configs.put(useCase, new ConfigPair(extendedConfig, cameraConfig));
1007         }
1008         return configs;
1009     }
1010 
1011     @NonNull
attachUseCaseSharedConfigs( @onNull UseCase useCase, @Nullable UseCaseConfig<?> useCaseConfig, @NonNull Range<Integer> targetHighSpeedFps)1012     private static UseCaseConfig<?> attachUseCaseSharedConfigs(
1013             @NonNull UseCase useCase,
1014             @Nullable UseCaseConfig<?> useCaseConfig,
1015             @NonNull Range<Integer> targetHighSpeedFps) {
1016         MutableOptionsBundle mutableConfig = useCaseConfig != null
1017                 ? MutableOptionsBundle.from(useCaseConfig) : MutableOptionsBundle.create();
1018 
1019         mutableConfig.insertOption(OPTION_TARGET_HIGH_SPEED_FRAME_RATE, targetHighSpeedFps);
1020 
1021         return useCase.getUseCaseConfigBuilder(mutableConfig).getUseCaseConfig();
1022     }
1023 
generateExtendedStreamSharingConfigFromPreview( @onNull UseCaseConfigFactory extendedFactory, @NonNull StreamSharing streamSharing)1024     private static UseCaseConfig<?> generateExtendedStreamSharingConfigFromPreview(
1025             @NonNull UseCaseConfigFactory extendedFactory, @NonNull StreamSharing streamSharing) {
1026         Preview preview = new Preview.Builder().build();
1027         Config previewConfig = preview.getDefaultConfig(false, extendedFactory);
1028         if (previewConfig == null) {
1029             return null;
1030         }
1031 
1032         // Remove OPTION_TARGET_CLASS, since its value would be "Preview".
1033         MutableOptionsBundle mutableConfig = MutableOptionsBundle.from(previewConfig);
1034         mutableConfig.removeOption(TargetConfig.OPTION_TARGET_CLASS);
1035 
1036         return streamSharing.getUseCaseConfigBuilder(mutableConfig).getUseCaseConfig();
1037     }
1038 
1039     /**
1040      * Checks for any unsupported feature combinations and throws an exception if found.
1041      *
1042      * @throws IllegalArgumentException if any feature combination is not supported.
1043      */
checkUnsupportedFeatureCombinationAndThrow(@onNull Collection<UseCase> useCases)1044     private void checkUnsupportedFeatureCombinationAndThrow(@NonNull Collection<UseCase> useCases)
1045             throws IllegalArgumentException {
1046         // TODO(b/309900490): since there are other places (e.g. SupportedSurfaceCombination in
1047         //  camera2) that feature combination constraints are enforced, it would be nice if they
1048         //  followed a similar pattern for checking constraints.
1049         if (hasExtension()) {
1050             if (hasNonSdrConfig(useCases)) {
1051                 throw new IllegalArgumentException("Extensions are only supported for use with "
1052                         + "standard dynamic range.");
1053             }
1054 
1055             if (hasRawImageCapture(useCases)) {
1056                 throw new IllegalArgumentException("Extensions are not supported for use with "
1057                         + "Raw image capture.");
1058             }
1059         }
1060 
1061         // TODO(b/322311893): throw exception to block feature combination of effect with Ultra
1062         //  HDR, until ImageProcessor and SurfaceProcessor can support JPEG/R format.
1063         synchronized (mLock) {
1064             if (!mEffects.isEmpty() && (hasUltraHdrImageCapture(useCases)
1065                     || hasRawImageCapture(useCases))) {
1066                 throw new IllegalArgumentException("Ultra HDR image and Raw capture does not "
1067                         + "support for use with CameraEffect.");
1068             }
1069         }
1070     }
1071 
hasNonSdrConfig(@onNull Collection<UseCase> useCases)1072     private static boolean hasNonSdrConfig(@NonNull Collection<UseCase> useCases) {
1073         for (UseCase useCase : useCases) {
1074             DynamicRange dynamicRange = useCase.getCurrentConfig().getDynamicRange();
1075             if (isNotSdr(dynamicRange)) {
1076                 return true;
1077             }
1078         }
1079         return false;
1080     }
1081 
isNotSdr(@onNull DynamicRange dynamicRange)1082     private static boolean isNotSdr(@NonNull DynamicRange dynamicRange) {
1083         boolean is10Bit = dynamicRange.getBitDepth() == BIT_DEPTH_10_BIT;
1084         boolean isHdr = dynamicRange.getEncoding() != ENCODING_SDR
1085                 && dynamicRange.getEncoding() != ENCODING_UNSPECIFIED;
1086 
1087         return is10Bit || isHdr;
1088     }
1089 
hasUltraHdrImageCapture(@onNull Collection<UseCase> useCases)1090     private static boolean hasUltraHdrImageCapture(@NonNull Collection<UseCase> useCases) {
1091         for (UseCase useCase : useCases) {
1092             if (!isImageCapture(useCase)) {
1093                 continue;
1094             }
1095 
1096             UseCaseConfig<?> config = useCase.getCurrentConfig();
1097             if (config.containsOption(OPTION_OUTPUT_FORMAT) && checkNotNull(
1098                     config.retrieveOption(OPTION_OUTPUT_FORMAT)) == OUTPUT_FORMAT_JPEG_ULTRA_HDR) {
1099                 return true;
1100             }
1101 
1102         }
1103         return false;
1104     }
1105 
hasRawImageCapture(@onNull Collection<UseCase> useCases)1106     private static boolean hasRawImageCapture(@NonNull Collection<UseCase> useCases) {
1107         for (UseCase useCase : useCases) {
1108             if (!isImageCapture(useCase)) {
1109                 continue;
1110             }
1111 
1112             UseCaseConfig<?> config = useCase.getCurrentConfig();
1113             if (config.containsOption(OPTION_OUTPUT_FORMAT)
1114                     && (checkNotNull(config.retrieveOption(OPTION_OUTPUT_FORMAT))
1115                     == OUTPUT_FORMAT_RAW)) {
1116                 return true;
1117             }
1118 
1119         }
1120         return false;
1121     }
1122 
1123     /**
1124      * An identifier for a {@link CameraUseCaseAdapter}.
1125      *
1126      * <p>This identifies the actual camera instances that are wrapped by the
1127      * CameraUseCaseAdapter and is used to determine if 2 different instances of
1128      * CameraUseCaseAdapter are actually equivalent.
1129      */
1130     @AutoValue
1131     public abstract static class CameraId {
1132         /** Creates a identifier for a {@link CameraUseCaseAdapter}. */
create(@onNull String cameraIdString, @NonNull Identifier cameraConfigId)1133         public static @NonNull CameraId create(@NonNull String cameraIdString,
1134                 @NonNull Identifier cameraConfigId) {
1135             return new AutoValue_CameraUseCaseAdapter_CameraId(cameraIdString, cameraConfigId);
1136         }
1137 
1138         /** Gets the camera ID string. */
getCameraIdString()1139         public abstract @NonNull String getCameraIdString();
1140         /** Gets the camera configuration. */
getCameraConfigId()1141         public abstract @NonNull Identifier getCameraConfigId();
1142     }
1143 
1144     /**
1145      * An exception thrown when the {@link CameraUseCaseAdapter} errors in one of its operations.
1146      */
1147     public static final class CameraException extends Exception {
CameraException()1148         public CameraException() {
1149             super();
1150         }
1151 
CameraException(@onNull String message)1152         public CameraException(@NonNull String message) {
1153             super(message);
1154         }
1155 
CameraException(@onNull Throwable cause)1156         public CameraException(@NonNull Throwable cause) {
1157             super(cause);
1158         }
1159     }
1160 
1161     ////////////////////////////////////////////////////////////////////////////////////////////////
1162     // Camera interface
1163     ////////////////////////////////////////////////////////////////////////////////////////////////
1164     @Override
getCameraControl()1165     public @NonNull CameraControl getCameraControl() {
1166         return mCameraInternal.getCameraControl();
1167     }
1168 
1169     @Override
getCameraInfo()1170     public @NonNull CameraInfo getCameraInfo() {
1171         return mCameraInternal.getCameraInfo();
1172     }
1173 
getSecondaryCameraInfo()1174     public @Nullable CameraInfo getSecondaryCameraInfo() {
1175         return mSecondaryCameraInternal != null ? mSecondaryCameraInternal.getCameraInfo() : null;
1176     }
1177 
1178     @Override
getExtendedConfig()1179     public @NonNull CameraConfig getExtendedConfig() {
1180         synchronized (mLock) {
1181             return mCameraConfig;
1182         }
1183     }
1184 
1185     @Override
isUseCasesCombinationSupported(boolean withStreamSharing, UseCase @NonNull ... useCases)1186     public boolean isUseCasesCombinationSupported(boolean withStreamSharing,
1187             UseCase @NonNull ... useCases) {
1188         Collection<UseCase> useCasesToVerify = Arrays.asList(useCases);
1189         if (withStreamSharing) {
1190             StreamSharing streamSharing = createOrReuseStreamSharing(useCasesToVerify, true);
1191             useCasesToVerify = calculateCameraUseCases(useCasesToVerify, null, streamSharing);
1192         }
1193         synchronized (mLock) {
1194             // If the UseCases exceed the resolutions then it will throw an exception
1195             try {
1196                 Map<UseCase, ConfigPair> configs = getConfigs(useCasesToVerify,
1197                         mCameraConfig.getUseCaseConfigFactory(), mUseCaseConfigFactory,
1198                         FRAME_RATE_RANGE_UNSPECIFIED);
1199                 calculateSuggestedStreamSpecs(
1200                         getCameraMode(),
1201                         mCameraInternal.getCameraInfoInternal(),
1202                         useCasesToVerify, emptyList(), configs);
1203             } catch (IllegalArgumentException e) {
1204                 return false;
1205             }
1206 
1207             return true;
1208         }
1209     }
1210 
1211     /**
1212      * Calculate the internal created placeholder UseCase for Extensions.
1213      *
1214      * @param appUseCases UseCase provided by the app.
1215      */
calculatePlaceholderForExtensions( @onNull Collection<UseCase> appUseCases, @Nullable StreamSharing streamSharing)1216     private @Nullable UseCase calculatePlaceholderForExtensions(
1217             @NonNull Collection<UseCase> appUseCases, @Nullable StreamSharing streamSharing) {
1218         synchronized (mLock) {
1219             // Replace children with StreamSharing before calculation.
1220             List<UseCase> useCasesToCheck = new ArrayList<>(appUseCases);
1221             if (streamSharing != null) {
1222                 useCasesToCheck.add(streamSharing);
1223                 useCasesToCheck.removeAll(streamSharing.getChildren());
1224             }
1225 
1226             // Perform calculation.
1227             UseCase placeholder = null;
1228             if (isCoexistingPreviewImageCaptureRequired()) {
1229                 if (isExtraPreviewRequired(useCasesToCheck)) {
1230                     if (isPreview(mPlaceholderForExtensions)) {
1231                         placeholder = mPlaceholderForExtensions;
1232                     } else {
1233                         placeholder = createExtraPreview();
1234                     }
1235                 } else if (isExtraImageCaptureRequired(useCasesToCheck)) {
1236                     if (isImageCapture(mPlaceholderForExtensions)) {
1237                         placeholder = mPlaceholderForExtensions;
1238                     } else {
1239                         placeholder = createExtraImageCapture();
1240                     }
1241                 }
1242             }
1243             return placeholder;
1244         }
1245     }
1246 
isCoexistingPreviewImageCaptureRequired()1247     private boolean isCoexistingPreviewImageCaptureRequired() {
1248         synchronized (mLock) {
1249             return mCameraConfig.getUseCaseCombinationRequiredRule()
1250                     == CameraConfig.REQUIRED_RULE_COEXISTING_PREVIEW_AND_IMAGE_CAPTURE;
1251         }
1252     }
1253 
1254     /**
1255      * Returns true if the input use case list contains a {@link ImageCapture} but does not
1256      * contain a {@link Preview}.
1257      *
1258      * <p> Note that {@link StreamSharing} provides preview output surface to
1259      * {@link SessionProcessor} and is therefore considered a {@link Preview}.
1260      */
isExtraPreviewRequired(@onNull Collection<UseCase> useCases)1261     private static boolean isExtraPreviewRequired(@NonNull Collection<UseCase> useCases) {
1262         boolean hasPreviewOrStreamSharing = false;
1263         boolean hasImageCapture = false;
1264 
1265         for (UseCase useCase : useCases) {
1266             if (isPreview(useCase) || isStreamSharing(useCase)) {
1267                 hasPreviewOrStreamSharing = true;
1268             } else if (isImageCapture(useCase)) {
1269                 hasImageCapture = true;
1270             }
1271         }
1272 
1273         return hasImageCapture && !hasPreviewOrStreamSharing;
1274     }
1275 
1276     /**
1277      * Returns true if the input use case list contains a {@link Preview} but does not contain an
1278      * {@link ImageCapture}.
1279      *
1280      * <p> Note that {@link StreamSharing} provides preview output surface to
1281      * {@link SessionProcessor} and is therefore considered a {@link Preview}.
1282      */
isExtraImageCaptureRequired(@onNull Collection<UseCase> useCases)1283     private static boolean isExtraImageCaptureRequired(@NonNull Collection<UseCase> useCases) {
1284         boolean hasPreviewOrStreamSharing = false;
1285         boolean hasImageCapture = false;
1286 
1287         for (UseCase useCase : useCases) {
1288             if (isPreview(useCase) || isStreamSharing(useCase)) {
1289                 hasPreviewOrStreamSharing = true;
1290             } else if (isImageCapture(useCase)) {
1291                 hasImageCapture = true;
1292             }
1293         }
1294 
1295         return hasPreviewOrStreamSharing && !hasImageCapture;
1296     }
1297 
hasVideoCapture(@onNull Collection<UseCase> useCases)1298     private static boolean hasVideoCapture(@NonNull Collection<UseCase> useCases) {
1299         for (UseCase useCase : useCases) {
1300             if (isVideoCapture(useCase)) {
1301                 return true;
1302             }
1303         }
1304         return false;
1305     }
1306 
isVideoCapture(@ullable UseCase useCase)1307     private static boolean isVideoCapture(@Nullable UseCase useCase) {
1308         if (useCase != null) {
1309             if (useCase.getCurrentConfig().containsOption(OPTION_CAPTURE_TYPE)) {
1310                 return useCase.getCurrentConfig().getCaptureType() == CaptureType.VIDEO_CAPTURE;
1311             } else {
1312                 Log.e(TAG, useCase + " UseCase does not have capture type.");
1313             }
1314 
1315         }
1316         return false;
1317     }
1318 
isPreview(@ullable UseCase useCase)1319     private static boolean isPreview(@Nullable UseCase useCase) {
1320         return useCase instanceof Preview;
1321     }
1322 
isImageCapture(@ullable UseCase useCase)1323     private static boolean isImageCapture(@Nullable UseCase useCase) {
1324         return useCase instanceof ImageCapture;
1325     }
1326 
createExtraPreview()1327     private Preview createExtraPreview() {
1328         Preview preview = new Preview.Builder().setTargetName("Preview-Extra").build();
1329 
1330         // Sets a SurfaceProvider to provide the needed surface and release it
1331         preview.setSurfaceProvider((surfaceRequest) -> {
1332             SurfaceTexture surfaceTexture = new SurfaceTexture(0);
1333             surfaceTexture.setDefaultBufferSize(surfaceRequest.getResolution().getWidth(),
1334                     surfaceRequest.getResolution().getHeight());
1335             surfaceTexture.detachFromGLContext();
1336             Surface surface = new Surface(surfaceTexture);
1337             surfaceRequest.provideSurface(surface,
1338                     CameraXExecutors.directExecutor(),
1339                     (surfaceResponse) -> {
1340                         surface.release();
1341                         surfaceTexture.release();
1342                     });
1343         });
1344 
1345         return preview;
1346     }
1347 
createExtraImageCapture()1348     private ImageCapture createExtraImageCapture() {
1349         return new ImageCapture.Builder().setTargetName("ImageCapture-Extra").build();
1350     }
1351 }
1352