1 /*
2  * Copyright 2019 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.impl;
18 
19 import android.hardware.camera2.CameraCaptureSession;
20 import android.hardware.camera2.CameraDevice;
21 import android.hardware.camera2.CameraDevice.StateCallback;
22 import android.hardware.camera2.CaptureRequest;
23 import android.hardware.camera2.params.InputConfiguration;
24 import android.hardware.camera2.params.SessionConfiguration;
25 import android.util.Range;
26 import android.util.Size;
27 
28 import androidx.camera.core.DynamicRange;
29 import androidx.camera.core.Logger;
30 import androidx.camera.core.MirrorMode;
31 import androidx.camera.core.impl.stabilization.StabilizationMode;
32 import androidx.camera.core.internal.HighSpeedFpsModifier;
33 import androidx.camera.core.internal.compat.workaround.SurfaceSorter;
34 
35 import com.google.auto.value.AutoValue;
36 
37 import org.jspecify.annotations.NonNull;
38 import org.jspecify.annotations.Nullable;
39 
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.Collection;
43 import java.util.Collections;
44 import java.util.LinkedHashSet;
45 import java.util.List;
46 import java.util.Set;
47 import java.util.concurrent.atomic.AtomicBoolean;
48 
49 /**
50  * Configurations needed for a capture session.
51  *
52  * <p>The SessionConfig contains all the {@link android.hardware.camera2} parameters that are
53  * required to initialize a {@link android.hardware.camera2.CameraCaptureSession} and issue a {@link
54  * CaptureRequest}.
55  */
56 public final class SessionConfig {
57     /** Regular session type. */
58     public static final int SESSION_TYPE_REGULAR = SessionConfiguration.SESSION_REGULAR;
59     /** High-speed session type. */
60     public static final int SESSION_TYPE_HIGH_SPEED = SessionConfiguration.SESSION_HIGH_SPEED;
61     /** The default session type. */
62     public static final int DEFAULT_SESSION_TYPE = SESSION_TYPE_REGULAR;
63     // Current supported session template values and the bigger index in the list, the
64     // priority is higher.
65     private static final List<Integer> SUPPORTED_TEMPLATE_PRIORITY = Arrays.asList(
66             CameraDevice.TEMPLATE_PREVIEW,
67             // TODO(230673983): Based on the framework assumptions, we prioritize video capture
68             //  and disable ZSL (fallback to regular) if both use cases are bound.
69             CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG,
70             CameraDevice.TEMPLATE_RECORD
71     );
72     /** The set of {@link OutputConfig} that data from the camera will be put into. */
73     private final List<OutputConfig> mOutputConfigs;
74     /** The {@link OutputConfig} for the postview. */
75     private final OutputConfig mPostviewOutputConfig;
76     /** The state callback for a {@link CameraDevice}. */
77     private final List<CameraDevice.StateCallback> mDeviceStateCallbacks;
78     /** The state callback for a {@link CameraCaptureSession}. */
79     private final List<CameraCaptureSession.StateCallback> mSessionStateCallbacks;
80     /** The callbacks used in single requests. */
81     private final List<CameraCaptureCallback> mSingleCameraCaptureCallbacks;
82     private final ErrorListener mErrorListener;
83     /** The configuration for building the {@link CaptureRequest} used for repeating requests. */
84     private final CaptureConfig mRepeatingCaptureConfig;
85     /** The type of the session */
86     private final int mSessionType;
87 
88     /**
89      * Immutable class to store an input configuration that is used to create a reprocessable
90      * capture session.
91      */
92     private @Nullable InputConfiguration mInputConfiguration;
93 
94     /**
95      * The output configuration associated with the {@link DeferrableSurface} that will be used to
96      * create the configuration needed to open a camera session. In camera2 this will be used to
97      * create the corresponding {@link android.hardware.camera2.params.OutputConfiguration}.
98      */
99     @SuppressWarnings("AutoValueImmutableFields")  // avoid extra dependency for ImmutableList.
100     @AutoValue
101     public abstract static class OutputConfig {
102         public static final int SURFACE_GROUP_ID_NONE = -1;
103 
104         /**
105          * Returns the surface associated with the {@link OutputConfig}.
106          */
getSurface()107         public abstract @NonNull DeferrableSurface getSurface();
108 
109         /**
110          * Returns the shared surfaces. If non-empty, surface sharing will be enabled and the
111          * shared surfaces will share the same memory buffer as the main surface returned in
112          * {@link #getSurface()}.
113          */
getSharedSurfaces()114         public abstract @NonNull List<DeferrableSurface> getSharedSurfaces();
115 
116         /**
117          * Returns the physical camera ID. By default it would be null. For cameras consisting of
118          * multiple physical cameras, this allows output to be redirected to specific physical
119          * camera.
120          */
getPhysicalCameraId()121         public abstract @Nullable String getPhysicalCameraId();
122 
123         /**
124          * Returns the mirror mode.
125          *
126          * @return {@link MirrorMode}
127          */
128         @MirrorMode.Mirror
getMirrorMode()129         public abstract int getMirrorMode();
130 
131         /**
132          * Returns the surface group ID. Default value is {@link #SURFACE_GROUP_ID_NONE} meaning
133          * it doesn't belong to any surface group. A surface group ID is used to identify which
134          * surface group this output surface belongs to. Output streams with the same
135          * non-negative group ID won't receive the camera output simultaneously therefore it
136          * could reduce the overall memory footprint.
137          */
getSurfaceGroupId()138         public abstract int getSurfaceGroupId();
139 
140         /**
141          * Returns the dynamic range for this output configuration.
142          *
143          * <p>The dynamic range will determine the dynamic range encoding and profile for pixels in
144          * the surfaces associated with this output configuration.
145          *
146          * <p>If not set, this defaults to {@link DynamicRange#SDR}.
147          */
getDynamicRange()148         public abstract @NonNull DynamicRange getDynamicRange();
149 
150         /**
151          * Creates the {@link Builder} instance with specified {@link DeferrableSurface}.
152          */
builder(@onNull DeferrableSurface surface)153         public static @NonNull Builder builder(@NonNull DeferrableSurface surface) {
154             return new AutoValue_SessionConfig_OutputConfig.Builder()
155                     .setSurface(surface)
156                     .setSharedSurfaces(Collections.emptyList())
157                     .setPhysicalCameraId(null)
158                     .setMirrorMode(MirrorMode.MIRROR_MODE_UNSPECIFIED)
159                     .setSurfaceGroupId(SURFACE_GROUP_ID_NONE)
160                     .setDynamicRange(DynamicRange.SDR);
161         }
162 
163         /**
164          * Builder to create the {@link OutputConfig}
165          */
166         @AutoValue.Builder
167         public abstract static class Builder {
168             /**
169              * Sets the surface associated with the {@link OutputConfig}.
170              */
setSurface(@onNull DeferrableSurface surface)171             public abstract @NonNull Builder setSurface(@NonNull DeferrableSurface surface);
172 
173             /**
174              * Sets the shared surfaces. After being set, surface sharing will be enabled and the
175              * shared surfaces will share the same memory buffer as the main surface returned in
176              * {@link #getSurface()}.
177              */
setSharedSurfaces( @onNull List<DeferrableSurface> surface)178             public abstract @NonNull Builder setSharedSurfaces(
179                     @NonNull List<DeferrableSurface> surface);
180 
181             /**
182              * Sets the physical camera ID. For cameras consisting of multiple physical cameras,
183              * this allows output to be redirected to specific physical camera.
184              */
setPhysicalCameraId(@ullable String cameraId)185             public abstract @NonNull Builder setPhysicalCameraId(@Nullable String cameraId);
186 
187             /**
188              * Sets the mirror mode. It specifies mirroring mode for
189              * {@link android.hardware.camera2.params.OutputConfiguration}.
190              * @see android.hardware.camera2.params.OutputConfiguration#setMirrorMode(int)
191              */
setMirrorMode(@irrorMode.Mirror int mirrorMode)192             public abstract @NonNull Builder setMirrorMode(@MirrorMode.Mirror int mirrorMode);
193 
194             /**
195              * Sets the surface group ID. A surface group ID is used to identify which surface group
196              * this output surface belongs to. Output streams with the same non-negative group ID
197              * won't receive the camera output simultaneously therefore it could be used to reduce
198              * the overall memory footprint.
199              */
setSurfaceGroupId(int surfaceGroupId)200             public abstract @NonNull Builder setSurfaceGroupId(int surfaceGroupId);
201 
202             /**
203              * Returns the dynamic range for this output configuration.
204              *
205              * <p>The dynamic range will determine the dynamic range encoding and profile for
206              * pixels in the surfaces associated with this output configuration.
207              */
setDynamicRange(@onNull DynamicRange dynamicRange)208             public abstract @NonNull Builder setDynamicRange(@NonNull DynamicRange dynamicRange);
209 
210             /**
211              * Creates the instance.
212              */
build()213             public abstract @NonNull OutputConfig build();
214         }
215     }
216 
217     /**
218      * Private constructor for a SessionConfig.
219      *
220      * <p>In practice, the {@link SessionConfig.BaseBuilder} will be used to construct a
221      * SessionConfig.
222      *
223      * @param outputConfigs          The list of {@link OutputConfig} where data will be put into.
224      * @param deviceStateCallbacks   The state callbacks for a {@link CameraDevice}.
225      * @param sessionStateCallbacks  The state callbacks for a {@link CameraCaptureSession}.
226      * @param repeatingCaptureConfig The configuration for building the {@link CaptureRequest}.
227      * @param inputConfiguration     The input configuration to create a reprocessable capture
228      *                               session.
229      * @param sessionType            The session type for the {@link CameraCaptureSession}.
230      */
SessionConfig( List<OutputConfig> outputConfigs, List<StateCallback> deviceStateCallbacks, List<CameraCaptureSession.StateCallback> sessionStateCallbacks, List<CameraCaptureCallback> singleCameraCaptureCallbacks, CaptureConfig repeatingCaptureConfig, @Nullable ErrorListener errorListener, @Nullable InputConfiguration inputConfiguration, int sessionType, @Nullable OutputConfig postviewOutputConfig)231     SessionConfig(
232             List<OutputConfig> outputConfigs,
233             List<StateCallback> deviceStateCallbacks,
234             List<CameraCaptureSession.StateCallback> sessionStateCallbacks,
235             List<CameraCaptureCallback> singleCameraCaptureCallbacks,
236             CaptureConfig repeatingCaptureConfig,
237             @Nullable ErrorListener errorListener,
238             @Nullable InputConfiguration inputConfiguration,
239             int sessionType,
240             @Nullable OutputConfig postviewOutputConfig) {
241         mOutputConfigs = outputConfigs;
242         mDeviceStateCallbacks = Collections.unmodifiableList(deviceStateCallbacks);
243         mSessionStateCallbacks = Collections.unmodifiableList(sessionStateCallbacks);
244         mSingleCameraCaptureCallbacks =
245                 Collections.unmodifiableList(singleCameraCaptureCallbacks);
246         mErrorListener = errorListener;
247         mRepeatingCaptureConfig = repeatingCaptureConfig;
248         mInputConfiguration = inputConfiguration;
249         mSessionType = sessionType;
250         mPostviewOutputConfig = postviewOutputConfig;
251     }
252 
253     /** Returns an instance of a session configuration with minimal configurations. */
defaultEmptySessionConfig()254     public static @NonNull SessionConfig defaultEmptySessionConfig() {
255         return new SessionConfig(
256                 new ArrayList<OutputConfig>(),
257                 new ArrayList<CameraDevice.StateCallback>(0),
258                 new ArrayList<CameraCaptureSession.StateCallback>(0),
259                 new ArrayList<CameraCaptureCallback>(0),
260                 new CaptureConfig.Builder().build(),
261                 /* errorListener */ null,
262                 /* inputConfiguration */ null,
263                 DEFAULT_SESSION_TYPE,
264                 /* postviewOutputConfig */ null);
265     }
266 
getInputConfiguration()267     public @Nullable InputConfiguration getInputConfiguration() {
268         return mInputConfiguration;
269     }
270 
271     /**
272      * Returns all {@link DeferrableSurface}s that are used to configure the session. It includes
273      * both the {@link DeferrableSurface} of the all {@link OutputConfig}s and its shared
274      * surfaces.
275      */
getSurfaces()276     public @NonNull List<DeferrableSurface> getSurfaces() {
277         List<DeferrableSurface> deferrableSurfaces = new ArrayList<>();
278         for (OutputConfig outputConfig : mOutputConfigs) {
279             deferrableSurfaces.add(outputConfig.getSurface());
280             for (DeferrableSurface sharedSurface : outputConfig.getSharedSurfaces()) {
281                 deferrableSurfaces.add(sharedSurface);
282             }
283         }
284         return Collections.unmodifiableList(deferrableSurfaces);
285     }
286 
getOutputConfigs()287     public @NonNull List<OutputConfig> getOutputConfigs() {
288         return mOutputConfigs;
289     }
290 
getPostviewOutputConfig()291     public @Nullable OutputConfig getPostviewOutputConfig() {
292         return mPostviewOutputConfig;
293     }
294 
getImplementationOptions()295     public @NonNull Config getImplementationOptions() {
296         return mRepeatingCaptureConfig.getImplementationOptions();
297     }
298 
getTemplateType()299     public int getTemplateType() {
300         return mRepeatingCaptureConfig.getTemplateType();
301     }
302 
getSessionType()303     public int getSessionType() {
304         return mSessionType;
305     }
306 
getExpectedFrameRateRange()307     public @NonNull Range<Integer> getExpectedFrameRateRange() {
308         return mRepeatingCaptureConfig.getExpectedFrameRateRange();
309     }
310 
311     /** Obtains all registered {@link CameraDevice.StateCallback} callbacks. */
getDeviceStateCallbacks()312     public @NonNull List<CameraDevice.StateCallback> getDeviceStateCallbacks() {
313         return mDeviceStateCallbacks;
314     }
315 
316     /** Obtains all registered {@link CameraCaptureSession.StateCallback} callbacks. */
getSessionStateCallbacks()317     public @NonNull List<CameraCaptureSession.StateCallback> getSessionStateCallbacks() {
318         return mSessionStateCallbacks;
319     }
320 
321     /** Obtains all registered {@link CameraCaptureCallback} callbacks for repeating requests. */
getRepeatingCameraCaptureCallbacks()322     public @NonNull List<CameraCaptureCallback> getRepeatingCameraCaptureCallbacks() {
323         return mRepeatingCaptureConfig.getCameraCaptureCallbacks();
324     }
325 
326     /** Obtains the registered {@link ErrorListener} callback. */
getErrorListener()327     public @Nullable ErrorListener getErrorListener() {
328         return mErrorListener;
329     }
330 
331     /** Obtains all registered {@link CameraCaptureCallback} callbacks for single requests. */
getSingleCameraCaptureCallbacks()332     public @NonNull List<CameraCaptureCallback> getSingleCameraCaptureCallbacks() {
333         return mSingleCameraCaptureCallbacks;
334     }
335 
getRepeatingCaptureConfig()336     public @NonNull CaptureConfig getRepeatingCaptureConfig() {
337         return mRepeatingCaptureConfig;
338     }
339 
340     /** Returns the one which has higher priority. */
getHigherPriorityTemplateType(int type1, int type2)341     public static int getHigherPriorityTemplateType(int type1, int type2) {
342         return SUPPORTED_TEMPLATE_PRIORITY.indexOf(type1)
343                 >= SUPPORTED_TEMPLATE_PRIORITY.indexOf(type2) ? type1 : type2;
344     }
345 
346     public enum SessionError {
347         /**
348          * A {@link DeferrableSurface} contained in the config needs to be reset.
349          *
350          * <p>The surface is no longer valid, for example the surface has already been closed.
351          */
352         SESSION_ERROR_SURFACE_NEEDS_RESET,
353         /** An unknown error has occurred. */
354         SESSION_ERROR_UNKNOWN
355     }
356 
357     /**
358      * Callback for errors that occur when accessing the session config.
359      */
360     public interface ErrorListener {
361         /**
362          * Called when an error has occurred.
363          *
364          * @param sessionConfig The {@link SessionConfig} that generated the error.
365          * @param error         The error that was generated.
366          */
onError(@onNull SessionConfig sessionConfig, @NonNull SessionError error)367         void onError(@NonNull SessionConfig sessionConfig, @NonNull SessionError error);
368     }
369 
370     /**
371      * A closeable ErrorListener that onError callback won't be invoked after it is closed.
372      */
373     public static final class CloseableErrorListener implements ErrorListener {
374         private final AtomicBoolean mIsClosed = new AtomicBoolean(false);
375         private final ErrorListener mErrorListener;
376 
CloseableErrorListener(@onNull ErrorListener errorListener)377         public CloseableErrorListener(@NonNull ErrorListener errorListener) {
378             mErrorListener = errorListener;
379         }
380 
381         @Override
onError(@onNull SessionConfig sessionConfig, @NonNull SessionError error)382         public void onError(@NonNull SessionConfig sessionConfig, @NonNull SessionError error) {
383             if (!mIsClosed.get()) {
384                 mErrorListener.onError(sessionConfig, error);
385             }
386         }
387 
388         /**
389          * Closes the ErrorListener to not invoke the onError callback function.
390          */
close()391         public void close() {
392             mIsClosed.set(true);
393         }
394     }
395 
396     /**
397      * Interface for unpacking a configuration into a SessionConfig.Builder
398      *
399      * <p>TODO(b/120949879): This will likely be removed once SessionConfig is refactored to
400      * remove camera2 dependencies.
401      */
402     public interface OptionUnpacker {
403 
404         /**
405          * Apply the options from the config onto the builder
406          *
407          * @param resolution the suggested resolution
408          * @param config  the set of options to apply
409          * @param builder the builder on which to apply the options
410          */
unpack( @onNull Size resolution, @NonNull UseCaseConfig<?> config, SessionConfig.@NonNull Builder builder)411         void unpack(
412                 @NonNull Size resolution,
413                 @NonNull UseCaseConfig<?> config,
414                 SessionConfig.@NonNull Builder builder);
415     }
416 
417     /**
418      * Base builder for easy modification/rebuilding of a {@link SessionConfig}.
419      */
420     static class BaseBuilder {
421         // Use LinkedHashSet to preserve the adding order for bug fixing and testing.
422         final Set<OutputConfig> mOutputConfigs = new LinkedHashSet<>();
423         final CaptureConfig.Builder mCaptureConfigBuilder = new CaptureConfig.Builder();
424         final List<CameraDevice.StateCallback> mDeviceStateCallbacks = new ArrayList<>();
425         final List<CameraCaptureSession.StateCallback> mSessionStateCallbacks = new ArrayList<>();
426         final List<CameraCaptureCallback> mSingleCameraCaptureCallbacks = new ArrayList<>();
427         @Nullable ErrorListener mErrorListener;
428         @Nullable InputConfiguration mInputConfiguration;
429         int mSessionType = DEFAULT_SESSION_TYPE;
430         @Nullable OutputConfig mPostviewOutputConfig;
431     }
432 
433     /**
434      * Builder for easy modification/rebuilding of a {@link SessionConfig}.
435      */
436     public static class Builder extends BaseBuilder {
437         /**
438          * Creates a {@link Builder} from a {@link UseCaseConfig}.
439          *
440          * <p>Populates the builder with all the properties defined in the base configuration.
441          */
createFrom( @onNull UseCaseConfig<?> config, @NonNull Size resolution)442         public static @NonNull Builder createFrom(
443                 @NonNull UseCaseConfig<?> config,
444                 @NonNull Size resolution) {
445             OptionUnpacker unpacker = config.getSessionOptionUnpacker(null);
446             if (unpacker == null) {
447                 throw new IllegalStateException(
448                         "Implementation is missing option unpacker for "
449                                 + config.getTargetName(config.toString()));
450             }
451 
452             Builder builder = new Builder();
453 
454             // Unpack the configuration into this builder
455             unpacker.unpack(resolution, config, builder);
456             return builder;
457         }
458 
459         /**
460          * Set the input configuration for reprocessable capture session.
461          *
462          * @param inputConfiguration The input configuration.
463          */
setInputConfiguration( @ullable InputConfiguration inputConfiguration)464         public @NonNull Builder setInputConfiguration(
465                 @Nullable InputConfiguration inputConfiguration) {
466             mInputConfiguration = inputConfiguration;
467             return this;
468         }
469 
470         /**
471          * Set the template characteristics of the SessionConfig.
472          *
473          * @param templateType Template constant that must match those defined by {@link
474          *                     CameraDevice}
475          *                     <p>TODO(b/120949879): This is camera2 implementation detail that
476          *                     should be moved
477          */
setTemplateType(int templateType)478         public @NonNull Builder setTemplateType(int templateType) {
479             mCaptureConfigBuilder.setTemplateType(templateType);
480             return this;
481         }
482 
483         /**
484          * Sets the session type.
485          */
setSessionType(int sessionType)486         public @NonNull Builder setSessionType(int sessionType) {
487             mSessionType = sessionType;
488             return this;
489         }
490 
491         /**
492          * Set the expected frame rate range of the SessionConfig.
493          *
494          * @param expectedFrameRateRange The frame rate range calculated from the UseCases for
495          *                               {@link CameraDevice}
496          */
setExpectedFrameRateRange( @onNull Range<Integer> expectedFrameRateRange)497         public @NonNull Builder setExpectedFrameRateRange(
498                 @NonNull Range<Integer> expectedFrameRateRange) {
499             mCaptureConfigBuilder.setExpectedFrameRateRange(expectedFrameRateRange);
500             return this;
501         }
502 
503         /**
504          * Set the preview stabilization mode of the SessionConfig.
505          * @param mode {@link StabilizationMode}
506          */
setPreviewStabilization(@tabilizationMode.Mode int mode)507         public @NonNull Builder setPreviewStabilization(@StabilizationMode.Mode int mode) {
508             if (mode != StabilizationMode.UNSPECIFIED) {
509                 mCaptureConfigBuilder.setPreviewStabilization(mode);
510             }
511             return this;
512         }
513 
514         /**
515          * Set the video stabilization mode of the SessionConfig.
516          * @param mode {@link StabilizationMode}
517          */
setVideoStabilization(@tabilizationMode.Mode int mode)518         public @NonNull Builder setVideoStabilization(@StabilizationMode.Mode int mode) {
519             if (mode != StabilizationMode.UNSPECIFIED) {
520                 mCaptureConfigBuilder.setVideoStabilization(mode);
521             }
522             return this;
523         }
524 
525         /**
526          * Adds a tag to the SessionConfig with a key. For tracking the source.
527          */
addTag(@onNull String key, @NonNull Object tag)528         public @NonNull Builder addTag(@NonNull String key, @NonNull Object tag) {
529             mCaptureConfigBuilder.addTag(key, tag);
530             return this;
531         }
532 
533         /**
534          * Adds a {@link CameraDevice.StateCallback} callback.
535          */
536         // TODO(b/120949879): This is camera2 implementation detail that should be moved
addDeviceStateCallback( CameraDevice.@onNull StateCallback deviceStateCallback)537         public @NonNull Builder addDeviceStateCallback(
538                 CameraDevice.@NonNull StateCallback deviceStateCallback) {
539             if (mDeviceStateCallbacks.contains(deviceStateCallback)) {
540                 return this;
541             }
542             mDeviceStateCallbacks.add(deviceStateCallback);
543             return this;
544         }
545 
546         /**
547          * Adds all {@link CameraDevice.StateCallback} callbacks.
548          */
addAllDeviceStateCallbacks( @onNull Collection<CameraDevice.StateCallback> deviceStateCallbacks)549         public @NonNull Builder addAllDeviceStateCallbacks(
550                 @NonNull Collection<CameraDevice.StateCallback> deviceStateCallbacks) {
551             for (CameraDevice.StateCallback callback : deviceStateCallbacks) {
552                 addDeviceStateCallback(callback);
553             }
554             return this;
555         }
556 
557         /**
558          * Adds a {@link CameraCaptureSession.StateCallback} callback.
559          */
560         // TODO(b/120949879): This is camera2 implementation detail that should be moved
addSessionStateCallback( CameraCaptureSession.@onNull StateCallback sessionStateCallback)561         public @NonNull Builder addSessionStateCallback(
562                 CameraCaptureSession.@NonNull StateCallback sessionStateCallback) {
563             if (mSessionStateCallbacks.contains(sessionStateCallback)) {
564                 return this;
565             }
566             mSessionStateCallbacks.add(sessionStateCallback);
567             return this;
568         }
569 
570         /**
571          * Adds all {@link CameraCaptureSession.StateCallback} callbacks.
572          */
addAllSessionStateCallbacks( @onNull List<CameraCaptureSession.StateCallback> sessionStateCallbacks)573         public @NonNull Builder addAllSessionStateCallbacks(
574                 @NonNull List<CameraCaptureSession.StateCallback> sessionStateCallbacks) {
575             for (CameraCaptureSession.StateCallback callback : sessionStateCallbacks) {
576                 addSessionStateCallback(callback);
577             }
578             return this;
579         }
580 
581         /**
582          * Adds a {@link CameraCaptureCallback} callback for repeating requests.
583          * <p>This callback does not call for single requests.
584          */
addRepeatingCameraCaptureCallback( @onNull CameraCaptureCallback cameraCaptureCallback)585         public @NonNull Builder addRepeatingCameraCaptureCallback(
586                 @NonNull CameraCaptureCallback cameraCaptureCallback) {
587             mCaptureConfigBuilder.addCameraCaptureCallback(cameraCaptureCallback);
588             return this;
589         }
590 
591         /**
592          * Adds all {@link CameraCaptureCallback} callbacks.
593          * <p>These callbacks do not call for single requests.
594          */
addAllRepeatingCameraCaptureCallbacks( @onNull Collection<CameraCaptureCallback> cameraCaptureCallbacks)595         public @NonNull Builder addAllRepeatingCameraCaptureCallbacks(
596                 @NonNull Collection<CameraCaptureCallback> cameraCaptureCallbacks) {
597             mCaptureConfigBuilder.addAllCameraCaptureCallbacks(cameraCaptureCallbacks);
598             return this;
599         }
600 
601         /**
602          * Adds a {@link CameraCaptureCallback} callback for single and repeating requests.
603          * <p>Listeners added here are available in both the
604          * {@link #getRepeatingCameraCaptureCallbacks()} and
605          * {@link #getSingleCameraCaptureCallbacks()} methods.
606          */
addCameraCaptureCallback( @onNull CameraCaptureCallback cameraCaptureCallback)607         public @NonNull Builder addCameraCaptureCallback(
608                 @NonNull CameraCaptureCallback cameraCaptureCallback) {
609             mCaptureConfigBuilder.addCameraCaptureCallback(cameraCaptureCallback);
610             if (!mSingleCameraCaptureCallbacks.contains(cameraCaptureCallback)) {
611                 mSingleCameraCaptureCallbacks.add(cameraCaptureCallback);
612             }
613             return this;
614         }
615 
616         /**
617          * Adds all {@link CameraCaptureCallback} callbacks for single and repeating requests.
618          * <p>Listeners added here are available in both the
619          * {@link #getRepeatingCameraCaptureCallbacks()} and
620          * {@link #getSingleCameraCaptureCallbacks()} methods.
621          */
addAllCameraCaptureCallbacks( @onNull Collection<CameraCaptureCallback> cameraCaptureCallbacks)622         public @NonNull Builder addAllCameraCaptureCallbacks(
623                 @NonNull Collection<CameraCaptureCallback> cameraCaptureCallbacks) {
624             for (CameraCaptureCallback c : cameraCaptureCallbacks) {
625                 mCaptureConfigBuilder.addCameraCaptureCallback(c);
626                 if (!mSingleCameraCaptureCallbacks.contains(c)) {
627                     mSingleCameraCaptureCallbacks.add(c);
628                 }
629             }
630             return this;
631         }
632 
633         /**
634          * Removes a previously added {@link CameraCaptureCallback} callback for single and/or
635          * repeating requests.
636          *
637          * @param cameraCaptureCallback The callback to remove.
638          * @return {@code true} if the callback was successfully removed. {@code false} if the
639          * callback wasn't present in this builder.
640          */
removeCameraCaptureCallback( @onNull CameraCaptureCallback cameraCaptureCallback)641         public boolean removeCameraCaptureCallback(
642                 @NonNull CameraCaptureCallback cameraCaptureCallback) {
643             boolean removedFromRepeating =
644                     mCaptureConfigBuilder.removeCameraCaptureCallback(cameraCaptureCallback);
645             boolean removedFromSingle =
646                     mSingleCameraCaptureCallbacks.remove(cameraCaptureCallback);
647             return removedFromRepeating || removedFromSingle;
648         }
649 
650         /** Obtain all {@link CameraCaptureCallback} callbacks for single requests. */
getSingleCameraCaptureCallbacks()651         public @NonNull List<CameraCaptureCallback> getSingleCameraCaptureCallbacks() {
652             return Collections.unmodifiableList(mSingleCameraCaptureCallbacks);
653         }
654 
655         /**
656          * Adds all {@link ErrorListener} listeners repeating requests.
657          */
setErrorListener(@onNull ErrorListener errorListener)658         public @NonNull Builder setErrorListener(@NonNull ErrorListener errorListener) {
659             mErrorListener = errorListener;
660             return this;
661         }
662 
663 
664         /**
665          * Add a surface to the set that the session repeatedly writes data to.
666          *
667          * <p>The dynamic range of this surface will default to {@link DynamicRange#SDR}. To
668          * manually set the dynamic range, use
669          * {@link #addSurface(DeferrableSurface, DynamicRange, String, int)}.
670          */
addSurface(@onNull DeferrableSurface surface)671         public @NonNull Builder addSurface(@NonNull DeferrableSurface surface) {
672             return addSurface(surface, DynamicRange.SDR, null,
673                     MirrorMode.MIRROR_MODE_UNSPECIFIED);
674         }
675 
676         /**
677          * Add a surface with the provided dynamic range to the set that the session repeatedly
678          * writes data to.
679          */
addSurface(@onNull DeferrableSurface surface, @NonNull DynamicRange dynamicRange, @Nullable String physicalCameraId, @MirrorMode.Mirror int mirrorMode)680         public @NonNull Builder addSurface(@NonNull DeferrableSurface surface,
681                 @NonNull DynamicRange dynamicRange,
682                 @Nullable String physicalCameraId,
683                 @MirrorMode.Mirror int mirrorMode) {
684             OutputConfig outputConfig = OutputConfig.builder(surface)
685                     .setPhysicalCameraId(physicalCameraId)
686                     .setDynamicRange(dynamicRange)
687                     .setMirrorMode(mirrorMode)
688                     .build();
689             mOutputConfigs.add(outputConfig);
690             mCaptureConfigBuilder.addSurface(surface);
691             return this;
692         }
693 
694         /**
695          * Adds an {@link OutputConfig} to create the capture session with. The surface set in
696          * the {@link OutputConfig} will be added to the repeating request.
697          */
addOutputConfig(@onNull OutputConfig outputConfig)698         public @NonNull Builder addOutputConfig(@NonNull OutputConfig outputConfig) {
699             mOutputConfigs.add(outputConfig);
700             mCaptureConfigBuilder.addSurface(outputConfig.getSurface());
701             for (DeferrableSurface sharedSurface : outputConfig.getSharedSurfaces()) {
702                 mCaptureConfigBuilder.addSurface(sharedSurface);
703             }
704             return this;
705         }
706 
707         /**
708          * Add a surface for the session which only used for single captures.
709          *
710          * <p>The dynamic range of this surface will default to {@link DynamicRange#SDR}. To
711          * manually set the dynamic range, use
712          * {@link #addNonRepeatingSurface(DeferrableSurface, DynamicRange)}.
713          */
addNonRepeatingSurface(@onNull DeferrableSurface surface)714         public @NonNull Builder addNonRepeatingSurface(@NonNull DeferrableSurface surface) {
715             return addNonRepeatingSurface(surface, DynamicRange.SDR);
716         }
717 
718         /**
719          * Add a surface with the provided dynamic range for the session which only used for
720          * single captures.
721          */
addNonRepeatingSurface(@onNull DeferrableSurface surface, @NonNull DynamicRange dynamicRange)722         public @NonNull Builder addNonRepeatingSurface(@NonNull DeferrableSurface surface,
723                 @NonNull DynamicRange dynamicRange) {
724             OutputConfig outputConfig = OutputConfig.builder(surface)
725                     .setDynamicRange(dynamicRange)
726                     .build();
727             mOutputConfigs.add(outputConfig);
728             return this;
729         }
730 
731         /**
732          * Sets the postview surface.
733          */
setPostviewSurface(@onNull DeferrableSurface surface)734         public @NonNull Builder setPostviewSurface(@NonNull DeferrableSurface surface) {
735             mPostviewOutputConfig = OutputConfig.builder(surface).build();
736             return this;
737         }
738 
739         /** Remove a surface from the set which the session repeatedly writes to. */
removeSurface(@onNull DeferrableSurface surface)740         public @NonNull Builder removeSurface(@NonNull DeferrableSurface surface) {
741             OutputConfig outputConfigToRemove = null;
742             for (OutputConfig config : mOutputConfigs) {
743                 if (config.getSurface().equals(surface)) {
744                     outputConfigToRemove = config;
745                     break;
746                 }
747             }
748 
749             if (outputConfigToRemove != null) {
750                 mOutputConfigs.remove(outputConfigToRemove);
751             }
752             mCaptureConfigBuilder.removeSurface(surface);
753             return this;
754         }
755 
756         /** Clears all surfaces from the set which the session writes to. */
clearSurfaces()757         public @NonNull Builder clearSurfaces() {
758             mOutputConfigs.clear();
759             mCaptureConfigBuilder.clearSurfaces();
760             return this;
761         }
762 
763         /** Set the {@link Config} for options that are implementation specific. */
setImplementationOptions(@onNull Config config)764         public @NonNull Builder setImplementationOptions(@NonNull Config config) {
765             mCaptureConfigBuilder.setImplementationOptions(config);
766             return this;
767         }
768 
769         /** Add a set of {@link Config} to the implementation specific options. */
addImplementationOptions(@onNull Config config)770         public @NonNull Builder addImplementationOptions(@NonNull Config config) {
771             mCaptureConfigBuilder.addImplementationOptions(config);
772             return this;
773         }
774 
775         /**
776          * Builds an instance of a SessionConfig that has all the combined parameters of the
777          * SessionConfig that have been added to the Builder.
778          */
build()779         public @NonNull SessionConfig build() {
780             return new SessionConfig(
781                     new ArrayList<>(mOutputConfigs),
782                     new ArrayList<>(mDeviceStateCallbacks),
783                     new ArrayList<>(mSessionStateCallbacks),
784                     new ArrayList<>(mSingleCameraCaptureCallbacks),
785                     mCaptureConfigBuilder.build(),
786                     mErrorListener,
787                     mInputConfiguration,
788                     mSessionType,
789                     mPostviewOutputConfig);
790         }
791     }
792 
793     /**
794      * Builder for combining multiple instances of {@link SessionConfig}. This will check if all
795      * the parameters for the {@link SessionConfig} are compatible with each other
796      */
797     public static final class ValidatingBuilder extends BaseBuilder {
798         private static final String TAG = "ValidatingBuilder";
799         private final SurfaceSorter mSurfaceSorter = new SurfaceSorter();
800         private boolean mValid = true;
801         private boolean mTemplateSet = false;
802         private List<ErrorListener> mErrorListeners = new ArrayList<>();
803 
804         /**
805          * Add an implementation option to the ValidatingBuilder's CaptureConfigBuilder. If it
806          * already has an option with the same key, write it over.
807          */
addImplementationOption(Config.@onNull Option<T> option, @NonNull T value)808         public <T> void addImplementationOption(Config.@NonNull Option<T> option,
809                 @NonNull T value) {
810             mCaptureConfigBuilder.addImplementationOption(option, value);
811         }
812 
813         /**
814          * Add the SessionConfig to the set of SessionConfig that have been aggregated by the
815          * ValidatingBuilder
816          */
add(@onNull SessionConfig sessionConfig)817         public void add(@NonNull SessionConfig sessionConfig) {
818             CaptureConfig captureConfig = sessionConfig.getRepeatingCaptureConfig();
819 
820             // Check template
821             if (captureConfig.getTemplateType() != CaptureConfig.TEMPLATE_TYPE_NONE) {
822                 mTemplateSet = true;
823                 mCaptureConfigBuilder.setTemplateType(
824                         getHigherPriorityTemplateType(captureConfig.getTemplateType(),
825                                 mCaptureConfigBuilder.getTemplateType()));
826             }
827 
828             setOrVerifyExpectFrameRateRange(captureConfig.getExpectedFrameRateRange());
829             setPreviewStabilizationMode(captureConfig.getPreviewStabilizationMode());
830             setVideoStabilizationMode(captureConfig.getVideoStabilizationMode());
831 
832             TagBundle tagBundle = sessionConfig.getRepeatingCaptureConfig().getTagBundle();
833             mCaptureConfigBuilder.addAllTags(tagBundle);
834 
835             // Check device state callbacks
836             mDeviceStateCallbacks.addAll(sessionConfig.getDeviceStateCallbacks());
837 
838             // Check session state callbacks
839             mSessionStateCallbacks.addAll(sessionConfig.getSessionStateCallbacks());
840 
841             // Check camera capture callbacks for repeating requests.
842             mCaptureConfigBuilder.addAllCameraCaptureCallbacks(
843                     sessionConfig.getRepeatingCameraCaptureCallbacks());
844 
845             // Check camera capture callbacks for single requests.
846             mSingleCameraCaptureCallbacks.addAll(sessionConfig.getSingleCameraCaptureCallbacks());
847 
848             if (sessionConfig.getErrorListener() != null) {
849                 mErrorListeners.add(sessionConfig.getErrorListener());
850             }
851 
852             // Check input configuration for reprocessable capture session.
853             if (sessionConfig.getInputConfiguration() != null) {
854                 mInputConfiguration = sessionConfig.getInputConfiguration();
855             }
856 
857             // Check surfaces
858             mOutputConfigs.addAll(sessionConfig.getOutputConfigs());
859 
860             // Check capture request surfaces
861             mCaptureConfigBuilder.getSurfaces().addAll(captureConfig.getSurfaces());
862 
863             if (!getSurfaces().containsAll(mCaptureConfigBuilder.getSurfaces())) {
864                 String errorMessage =
865                         "Invalid configuration due to capture request surfaces are not a subset "
866                                 + "of surfaces";
867                 Logger.d(TAG, errorMessage);
868                 mValid = false;
869             }
870 
871             if (sessionConfig.getSessionType() != mSessionType
872                     && sessionConfig.getSessionType() != DEFAULT_SESSION_TYPE
873                     && mSessionType != DEFAULT_SESSION_TYPE) {
874                 String errorMessage =
875                         "Invalid configuration due to that two non-default session types are set";
876                 Logger.d(TAG, errorMessage);
877                 mValid = false;
878             } else {
879                 if (sessionConfig.getSessionType() != DEFAULT_SESSION_TYPE) {
880                     mSessionType = sessionConfig.getSessionType();
881                 }
882             }
883 
884             if (sessionConfig.mPostviewOutputConfig != null) {
885                 if (mPostviewOutputConfig != sessionConfig.mPostviewOutputConfig
886                         && mPostviewOutputConfig != null) {
887                     String errorMessage =
888                             "Invalid configuration due to that two different postview output "
889                                     + "configs are set";
890                     Logger.d(TAG, errorMessage);
891                     mValid = false;
892                 } else {
893                     mPostviewOutputConfig = sessionConfig.mPostviewOutputConfig;
894                 }
895             }
896 
897             // The conflicting of options is handled in addImplementationOptions where it could
898             // throw an IllegalArgumentException if the conflict cannot be resolved.
899             mCaptureConfigBuilder.addImplementationOptions(
900                     captureConfig.getImplementationOptions());
901         }
902 
setOrVerifyExpectFrameRateRange( @onNull Range<Integer> expectedFrameRateRange)903         private void setOrVerifyExpectFrameRateRange(
904                 @NonNull Range<Integer> expectedFrameRateRange) {
905             if (expectedFrameRateRange.equals(StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED)) {
906                 return;
907             }
908 
909             if (mCaptureConfigBuilder.getExpectedFrameRateRange().equals(
910                     StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED)) {
911                 mCaptureConfigBuilder.setExpectedFrameRateRange(expectedFrameRateRange);
912                 return;
913             }
914 
915             if (!mCaptureConfigBuilder.getExpectedFrameRateRange().equals(expectedFrameRateRange)) {
916                 mValid = false;
917                 Logger.d(TAG, "Different ExpectedFrameRateRange values");
918             }
919         }
920 
setPreviewStabilizationMode(@tabilizationMode.Mode int mode)921         private void setPreviewStabilizationMode(@StabilizationMode.Mode int mode) {
922             if (mode != StabilizationMode.UNSPECIFIED) {
923                 mCaptureConfigBuilder.setPreviewStabilization(mode);
924             }
925         }
926 
setVideoStabilizationMode(@tabilizationMode.Mode int mode)927         private void setVideoStabilizationMode(@StabilizationMode.Mode int mode) {
928             if (mode != StabilizationMode.UNSPECIFIED) {
929                 mCaptureConfigBuilder.setVideoStabilization(mode);
930             }
931         }
932 
getSurfaces()933         private List<DeferrableSurface> getSurfaces() {
934             List<DeferrableSurface> surfaces = new ArrayList<>();
935             for (OutputConfig outputConfig : mOutputConfigs) {
936                 surfaces.add(outputConfig.getSurface());
937                 for (DeferrableSurface sharedSurface : outputConfig.getSharedSurfaces()) {
938                     surfaces.add(sharedSurface);
939                 }
940             }
941             return surfaces;
942         }
943 
944         /** Clears all surfaces from the set which the session writes to. */
clearSurfaces()945         public void clearSurfaces() {
946             mOutputConfigs.clear();
947             mCaptureConfigBuilder.clearSurfaces();
948         }
949 
950         /** Check if the set of SessionConfig that have been combined are valid */
isValid()951         public boolean isValid() {
952             return mTemplateSet && mValid;
953         }
954 
955         /**
956          * Builds an instance of a SessionConfig that has all the combined parameters of the
957          * SessionConfig that have been added to the ValidatingBuilder.
958          */
build()959         public @NonNull SessionConfig build() {
960             if (!mValid) {
961                 throw new IllegalArgumentException("Unsupported session configuration combination");
962             }
963 
964             List<OutputConfig> outputConfigs = new ArrayList<>(mOutputConfigs);
965             mSurfaceSorter.sort(outputConfigs);
966 
967             if (mSessionType == SESSION_TYPE_HIGH_SPEED) {
968                 // HighSpeedFpsModifier may modify the expected frame rate range for
969                 // mCaptureConfigBuilder.
970                 new HighSpeedFpsModifier().modifyFpsForPreviewOnlyRepeating(outputConfigs,
971                         mCaptureConfigBuilder);
972             }
973 
974             ErrorListener errorListener = null;
975             // Creates an error listener to notify errors to the underlying error listeners.
976             if (!mErrorListeners.isEmpty()) {
977                 errorListener = (sessionConfig, error) -> {
978                     for (ErrorListener listener: mErrorListeners) {
979                         listener.onError(sessionConfig, error);
980                     }
981                 };
982             }
983 
984             return new SessionConfig(
985                     outputConfigs,
986                     new ArrayList<>(mDeviceStateCallbacks),
987                     new ArrayList<>(mSessionStateCallbacks),
988                     new ArrayList<>(mSingleCameraCaptureCallbacks),
989                     mCaptureConfigBuilder.build(),
990                     errorListener,
991                     mInputConfiguration,
992                     mSessionType,
993                     mPostviewOutputConfig);
994         }
995     }
996 }
997