1 /*
2  * Copyright (C) 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 package androidx.camera.extensions;
17 
18 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
19 
20 import android.content.Context;
21 import android.hardware.camera2.CameraMetadata;
22 import android.hardware.camera2.params.StreamConfigurationMap;
23 import android.util.Range;
24 
25 import androidx.annotation.GuardedBy;
26 import androidx.annotation.RestrictTo;
27 import androidx.annotation.VisibleForTesting;
28 import androidx.camera.core.CameraControl;
29 import androidx.camera.core.CameraInfo;
30 import androidx.camera.core.CameraProvider;
31 import androidx.camera.core.CameraSelector;
32 import androidx.camera.core.DynamicRange;
33 import androidx.camera.core.ImageAnalysis;
34 import androidx.camera.core.ImageCapture;
35 import androidx.camera.core.Logger;
36 import androidx.camera.core.Preview;
37 import androidx.camera.core.impl.ExtendedCameraConfigProviderStore;
38 import androidx.camera.core.impl.utils.ContextUtil;
39 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
40 import androidx.camera.core.impl.utils.futures.Futures;
41 import androidx.camera.extensions.impl.InitializerImpl;
42 import androidx.camera.extensions.internal.ClientVersion;
43 import androidx.camera.extensions.internal.ExtensionVersion;
44 import androidx.camera.extensions.internal.Version;
45 import androidx.concurrent.futures.CallbackToFutureAdapter;
46 import androidx.lifecycle.LifecycleOwner;
47 
48 import com.google.common.util.concurrent.ListenableFuture;
49 
50 import org.jspecify.annotations.NonNull;
51 import org.jspecify.annotations.Nullable;
52 
53 import java.util.concurrent.ExecutionException;
54 
55 /**
56  * Provides interfaces for third party app developers to get capabilities info of extension
57  * functions.
58  *
59  * <p>Many Android devices contain powerful cameras, with manufacturers devoting a lot of effort
60  * to build many cutting-edge features, or special effects, into these camera devices.
61  * <code>CameraX Extensions</code> allows third party apps to enable the available extension
62  * modes on the supported devices. The extension modes which might be supported via <code>CameraX
63  * Extensions</code> are {@link ExtensionMode#BOKEH}, {@link ExtensionMode#HDR},
64  * {@link ExtensionMode#NIGHT}, {@link ExtensionMode#FACE_RETOUCH} and {@link ExtensionMode#AUTO}
65  * . The known supported devices are listed in the
66  * <a href="https://developer.android.com/training/camera/supported-devices">Supported devices</a>
67  * page.
68  *
69  * <p><code>CameraX Extensions</code> are built on the top of <code>CameraX Core</code> libraries
70  * . To enable an extension mode, an {@link ExtensionsManager} instance needs to be retrieved
71  * first with {@link #getInstanceAsync(Context, CameraProvider)}. Only a single
72  * {@link ExtensionsManager} instance can exist within a process. After retrieving the
73  * {@link ExtensionsManager} instance, the availability of a specific extension mode can be
74  * checked by {@link #isExtensionAvailable(CameraSelector, int)}. For an available extension
75  * mode, an extension enabled {@link CameraSelector} can be obtained by calling
76  * {@link #getExtensionEnabledCameraSelector(CameraSelector, int)}. After binding use cases by
77  * the extension enabled {@link CameraSelector}, the extension mode will be applied to the bound
78  * {@link Preview} and {@link ImageCapture}. The following sample code describes how to enable an
79  * extension mode for use cases.
80  * </p>
81  * <pre>
82  * void bindUseCasesWithBokehMode() {
83  *     // Create a camera provider
84  *     ProcessCameraProvider cameraProvider = ... // Get the provider instance
85  *     // Call the getInstance function to retrieve a ListenableFuture object
86  *     ListenableFuture future = ExtensionsManager.getInstance(context, cameraProvider);
87  *
88  *     // Obtain the ExtensionsManager instance from the returned ListenableFuture object
89  *     future.addListener(() -> {
90  *         try {
91  *             ExtensionsManager extensionsManager = future.get()
92  *
93  *             // Query if extension is available.
94  *             if (mExtensionsManager.isExtensionAvailable(DEFAULT_BACK_CAMERA,
95  *                        ExtensionMode.BOKEH)) {
96  *                 // Needs to unbind all use cases before enabling different extension mode.
97  *                 cameraProvider.unbindAll();
98  *
99  *                 // Retrieve extension enabled camera selector
100  *                 CameraSelector extensionCameraSelector;
101  *                 extensionCameraSelector = extensionsManager.getExtensionEnabledCameraSelector(
102  *                         DEFAULT_BACK_CAMERA, ExtensionMode.BOKEH);
103  *
104  *                 // Bind image capture and preview use cases with the extension enabled camera
105  *                 // selector.
106  *                 ImageCapture imageCapture = new ImageCapture.Builder().build();
107  *                 Preview preview = new Preview.Builder().build();
108  *                 cameraProvider.bindToLifecycle(lifecycleOwner, extensionCameraSelector,
109  *                         imageCapture, preview);
110  *             }
111  *         } catch (ExecutionException | InterruptedException e) {
112  *             // This should not happen unless the future is cancelled or the thread is
113  *             // interrupted by applications.
114  *         }
115  *     }, ContextCompact.getMainExecutor(context));
116  * }
117  * </pre>
118  *
119  * <p>Without enabling <code>CameraX Extensions</code>, any device should be able to support the
120  * use cases combination of {@link ImageCapture}, {@link Preview} and {@link ImageAnalysis}. To
121  * support the <code>CameraX Extensions</code> functionality, the {@link ImageCapture} or
122  * {@link Preview} might need to occupy a different format of stream. This might restrict the app
123  * to not be able to bind {@link ImageCapture}, {@link Preview} and {@link ImageAnalysis} at the
124  * same time if the device's hardware level is not
125  * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL} or above. If enabling an extension
126  * mode is more important and the {@link ImageAnalysis} could be optional to the app design, the
127  * extension mode can be enabled successfully when only binding {@link ImageCapture},
128  * {@link Preview} even if the device's hardware level is
129  * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED}.
130  *
131  * <p>While <code>CameraX Extensions</code> dose not directly support
132  * {@linkplain androidx.camera.video.VideoCapture},
133  * {@linkplain androidx.camera.video.VideoCapture} can still be used when any extension mode is
134  * enabled. When the app binds {@linkplain androidx.camera.video.VideoCapture} and enables any
135  * extension mode, {@linkplain androidx.camera.video.VideoCapture} can obtain the shared stream of
136  * {@link Preview} and record it as a video.
137  *
138  * <p>For some devices, the vendor library implementation might only support a subset of the all
139  * supported sizes retrieved by {@link StreamConfigurationMap#getOutputSizes(int)}. <code>CameraX
140  * </code> will select the supported sizes for the use cases according to the use cases'
141  * configuration and combination.
142  */
143 public final class ExtensionsManager {
144     private static final String TAG = "ExtensionsManager";
145 
146     enum ExtensionsAvailability {
147         /**
148          * The device extensions library exists and has been correctly loaded.
149          */
150         LIBRARY_AVAILABLE,
151         /**
152          * The device extensions library exists. However, there was some error loading the library.
153          */
154         LIBRARY_UNAVAILABLE_ERROR_LOADING,
155         /**
156          * The device extensions library exists. However, the library is missing implementations.
157          */
158         LIBRARY_UNAVAILABLE_MISSING_IMPLEMENTATION,
159         /**
160          * There are no extensions available on this device.
161          */
162         NONE
163     }
164 
165     // Singleton instance of the Extensions object
166     private static final Object EXTENSIONS_LOCK = new Object();
167 
168     @GuardedBy("EXTENSIONS_LOCK")
169     private static ListenableFuture<ExtensionsManager> sInitializeFuture;
170 
171     @GuardedBy("EXTENSIONS_LOCK")
172     private static ListenableFuture<Void> sDeinitializeFuture;
173 
174     @GuardedBy("EXTENSIONS_LOCK")
175     private static ExtensionsManager sExtensionsManager;
176 
177     private final ExtensionsAvailability mExtensionsAvailability;
178 
179     private final ExtensionsInfo mExtensionsInfo;
180 
181     /**
182      * Retrieves the {@link ExtensionsManager} associated with the current process.
183      *
184      * <p>An application must wait until the {@link ListenableFuture} completes to get an
185      * {@link ExtensionsManager} instance. The {@link ExtensionsManager} instance can be used to
186      * access the extensions related functions.
187      *
188      * @param context The context to initialize the extensions library.
189      * @param cameraProvider     A {@link CameraProvider} will be used to query the information
190      *                           of cameras on the device. The {@link CameraProvider} can be the
191      *                           {@link androidx.camera.lifecycle.ProcessCameraProvider}
192      *                           which is obtained by
193      *                 {@link androidx.camera.lifecycle.ProcessCameraProvider#getInstance(Context)}.
194      */
getInstanceAsync( @onNull Context context, @NonNull CameraProvider cameraProvider)195     public static @NonNull ListenableFuture<ExtensionsManager> getInstanceAsync(
196             @NonNull Context context, @NonNull CameraProvider cameraProvider) {
197         return getInstanceAsync(context, cameraProvider, ClientVersion.getCurrentVersion());
198     }
199 
200 
201     /**
202      * Retrieves the {@link ExtensionsManager} associated with the current process and
203      * initializes with the given client extensions-interface version.
204      *
205      * <p>This is for testing purpose. Since CameraX uses the latest extensions-interface
206      * version, we need a way to emulate the earlier version to see if OEM implementation can be
207      * compatible. For example, CameraX uses 1.3.0 and OEM implements 1.3.0 as well. We can use
208      * this API to emulate the situation that CameraX uses 1.2.0 and invokes the older version of
209      * API.
210      *
211      * @param context The context to initialize the extensions library.
212      * @param cameraProvider     A {@link CameraProvider} will be used to query the information
213      *                           of cameras on the device. The {@link CameraProvider} can be the
214      *                           {@link androidx.camera.lifecycle.ProcessCameraProvider}
215      *                           which is obtained by*
216      *                 {@link androidx.camera.lifecycle.ProcessCameraProvider#getInstance(Context)}.
217      * @param clientVersionStr the extensions-interface version used to initialize the extensions.
218      */
219     @RestrictTo(LIBRARY_GROUP)
220     @VisibleForTesting
getInstanceAsync( @onNull Context context, @NonNull CameraProvider cameraProvider, @NonNull String clientVersionStr)221     public static @NonNull ListenableFuture<ExtensionsManager> getInstanceAsync(
222             @NonNull Context context, @NonNull CameraProvider cameraProvider,
223             @NonNull String clientVersionStr) {
224         ClientVersion clientVersion = new ClientVersion(clientVersionStr);
225         ClientVersion.setCurrentVersion(clientVersion);
226         return getInstanceAsync(context, cameraProvider, clientVersion);
227     }
228 
getInstanceAsync(@onNull Context context, @NonNull CameraProvider cameraProvider, @NonNull ClientVersion clientVersion)229     static @NonNull ListenableFuture<ExtensionsManager> getInstanceAsync(@NonNull Context context,
230             @NonNull CameraProvider cameraProvider, @NonNull ClientVersion clientVersion) {
231         synchronized (EXTENSIONS_LOCK) {
232             if (sDeinitializeFuture != null && !sDeinitializeFuture.isDone()) {
233                 throw new IllegalStateException("Not yet done deinitializing extensions");
234             }
235             sDeinitializeFuture = null;
236             Context applicationContext = ContextUtil.getApplicationContext(context);
237 
238             // Will be initialized, with an empty implementation which will report all extensions
239             // as unavailable
240             if (ExtensionVersion.getRuntimeVersion() == null) {
241                 return Futures.immediateFuture(
242                         getOrCreateExtensionsManager(ExtensionsAvailability.NONE, cameraProvider,
243                                 applicationContext));
244             }
245 
246             // Prior to 1.1 no additional initialization logic required
247             if (ClientVersion.isMaximumCompatibleVersion(Version.VERSION_1_0)
248                     || ExtensionVersion.isMaximumCompatibleVersion(Version.VERSION_1_0)) {
249                 return Futures.immediateFuture(
250                         getOrCreateExtensionsManager(ExtensionsAvailability.LIBRARY_AVAILABLE,
251                                 cameraProvider, applicationContext));
252             }
253 
254             if (sInitializeFuture == null) {
255                 sInitializeFuture = CallbackToFutureAdapter.getFuture(completer -> {
256                     try {
257                         InitializerImpl.init(clientVersion.toVersionString(),
258                                 applicationContext,
259                                 new InitializerImpl.OnExtensionsInitializedCallback() {
260                                     @Override
261                                     public void onSuccess() {
262                                         Logger.d(TAG, "Successfully initialized extensions");
263                                         completer.set(getOrCreateExtensionsManager(
264                                                 ExtensionsAvailability.LIBRARY_AVAILABLE,
265                                                 cameraProvider, applicationContext));
266                                     }
267 
268                                     @Override
269                                     public void onFailure(int error) {
270                                         Logger.e(TAG, "Failed to initialize extensions");
271                                         completer.set(getOrCreateExtensionsManager(
272                                                 ExtensionsAvailability
273                                                         .LIBRARY_UNAVAILABLE_ERROR_LOADING,
274                                                 cameraProvider, applicationContext));
275                                     }
276                                 },
277                                 CameraXExecutors.directExecutor());
278                     } catch (NoSuchMethodError | NoClassDefFoundError | AbstractMethodError e) {
279                         Logger.e(TAG, "Failed to initialize extensions. Some classes or methods "
280                                 + "are missed in the vendor library. " + e);
281                         completer.set(getOrCreateExtensionsManager(
282                                 ExtensionsAvailability.LIBRARY_UNAVAILABLE_MISSING_IMPLEMENTATION,
283                                 cameraProvider, applicationContext));
284                     } catch (RuntimeException e) {
285                         // Catches all unexpected runtime exceptions and still returns an
286                         // ExtensionsManager instance which performs default behavior.
287                         Logger.e(TAG,
288                                 "Failed to initialize extensions. Something wents wrong when "
289                                         + "initializing the vendor library. "
290                                         + e);
291                         completer.set(getOrCreateExtensionsManager(
292                                 ExtensionsAvailability.LIBRARY_UNAVAILABLE_ERROR_LOADING,
293                                 cameraProvider, applicationContext));
294                     }
295 
296                     return "Initialize extensions";
297                 });
298             }
299 
300             return sInitializeFuture;
301         }
302     }
303 
304     /**
305      * Shutdown the extensions.
306      *
307      * <p> For the moment only used for testing to shutdown the extensions. Calling this function
308      * can deinitialize the extensions vendor library and release the created
309      * {@link ExtensionsManager} instance. Tests should wait until the returned future is
310      * complete. Then, tests can call the
311      * {@link ExtensionsManager#getInstanceAsync(Context, CameraProvider)} function again to
312      * initialize a new {@link ExtensionsManager} instance.
313      */
314     // TODO: Will need to be rewritten to be threadsafe with use in conjunction with
315     //  ExtensionsManager.init(...) if this is to be released for use outside of testing.
316     @VisibleForTesting
317     @RestrictTo(LIBRARY_GROUP)
shutdown()318     public @NonNull ListenableFuture<Void> shutdown() {
319         synchronized (EXTENSIONS_LOCK) {
320             if (ExtensionVersion.getRuntimeVersion() == null) {
321                 sInitializeFuture = null;
322                 sExtensionsManager = null;
323                 ExtensionVersion.injectInstance(null);
324                 return Futures.immediateFuture(null);
325             }
326 
327             // Reset the ExtensionsVersion.
328             ExtensionVersion.injectInstance(null);
329 
330             // If initialization not yet attempted then deinit should succeed immediately.
331             if (sInitializeFuture == null) {
332                 return Futures.immediateFuture(null);
333             }
334 
335             // If already in progress of deinit then return the future
336             if (sDeinitializeFuture != null) {
337                 return sDeinitializeFuture;
338             }
339 
340             ExtensionsAvailability availability;
341 
342             // Wait for the extension to be initialized before deinitializing. Block since
343             // this is only used for testing.
344             try {
345                 sInitializeFuture.get();
346                 sInitializeFuture = null;
347                 availability = sExtensionsManager.mExtensionsAvailability;
348                 sExtensionsManager = null;
349             } catch (ExecutionException | InterruptedException e) {
350                 sDeinitializeFuture = Futures.immediateFailedFuture(e);
351                 return sDeinitializeFuture;
352             }
353 
354             // Once extension has been initialized start the deinit call
355             if (availability == ExtensionsAvailability.LIBRARY_AVAILABLE) {
356                 ExtendedCameraConfigProviderStore.clear();
357                 sDeinitializeFuture = CallbackToFutureAdapter.getFuture(completer -> {
358                     try {
359                         InitializerImpl.deinit(
360                                 new InitializerImpl.OnExtensionsDeinitializedCallback() {
361                                     @Override
362                                     public void onSuccess() {
363                                         completer.set(null);
364                                     }
365 
366                                     @Override
367                                     public void onFailure(int error) {
368                                         completer.setException(new Exception("Failed to "
369                                                 + "deinitialize extensions."));
370                                     }
371                                 },
372                                 CameraXExecutors.directExecutor());
373                     } catch (NoSuchMethodError | NoClassDefFoundError e) {
374                         completer.setException(e);
375                     }
376                     return null;
377                 });
378             } else {
379                 sDeinitializeFuture = Futures.immediateFuture(null);
380             }
381             return sDeinitializeFuture;
382         }
383     }
384 
getOrCreateExtensionsManager( @onNull ExtensionsAvailability extensionsAvailability, @NonNull CameraProvider cameraProvider, @NonNull Context applicationContext)385     static ExtensionsManager getOrCreateExtensionsManager(
386             @NonNull ExtensionsAvailability extensionsAvailability,
387             @NonNull CameraProvider cameraProvider,
388             @NonNull Context applicationContext) {
389         synchronized (EXTENSIONS_LOCK) {
390             if (sExtensionsManager != null) {
391                 return sExtensionsManager;
392             }
393 
394             sExtensionsManager = new ExtensionsManager(extensionsAvailability, cameraProvider,
395                     applicationContext);
396 
397             return sExtensionsManager;
398         }
399     }
400 
401     /**
402      * Returns a modified {@link CameraSelector} that will enable the specified extension mode.
403      *
404      * <p>The returned extension {@link CameraSelector} can be used to bind use cases to a
405      * desired {@link LifecycleOwner} and then the specified extension mode will be enabled on
406      * the camera.
407      *
408      * @param baseCameraSelector The base {@link CameraSelector} on top of which the extension
409      *                           config is applied.
410      *                           {@link #isExtensionAvailable(CameraSelector, int)} can be used
411      *                           to check whether any camera can support the specified extension
412      *                           mode for the base camera selector.
413      * @param mode               The target extension mode.
414      * @return a {@link CameraSelector} for the specified Extensions mode.
415      * @throws IllegalArgumentException If this device doesn't support extensions function, no
416      *                                  camera can be found to support the specified extension
417      *                                  mode, or the base {@link CameraSelector} has contained
418      *                                  extension related configuration in it.
419      */
getExtensionEnabledCameraSelector( @onNull CameraSelector baseCameraSelector, @ExtensionMode.Mode int mode)420     public @NonNull CameraSelector getExtensionEnabledCameraSelector(
421             @NonNull CameraSelector baseCameraSelector, @ExtensionMode.Mode int mode) {
422         // Directly return the input baseCameraSelector if the target extension mode is NONE.
423         if (mode == ExtensionMode.NONE) {
424             return baseCameraSelector;
425         }
426 
427         if (mExtensionsAvailability != ExtensionsAvailability.LIBRARY_AVAILABLE) {
428             throw new IllegalArgumentException("This device doesn't support extensions function! "
429                     + "isExtensionAvailable should be checked first before calling "
430                     + "getExtensionEnabledCameraSelector.");
431         }
432 
433         return mExtensionsInfo.getExtensionCameraSelectorAndInjectCameraConfig(baseCameraSelector,
434                 mode);
435     }
436 
437     /**
438      * Returns true if the particular extension mode is available for the specified
439      * {@link CameraSelector}.
440      *
441      * <p> Note that Extensions are not supported for use with 10-bit capture output (e.g.
442      * setting a dynamic range other than {@link DynamicRange#SDR}).
443      *
444      * @param baseCameraSelector The base {@link CameraSelector} to find a camera to use.
445      * @param mode               The target extension mode to support.
446      */
isExtensionAvailable(@onNull CameraSelector baseCameraSelector, @ExtensionMode.Mode int mode)447     public boolean isExtensionAvailable(@NonNull CameraSelector baseCameraSelector,
448             @ExtensionMode.Mode int mode) {
449         if (mode == ExtensionMode.NONE) {
450             return true;
451         }
452 
453         if (mExtensionsAvailability != ExtensionsAvailability.LIBRARY_AVAILABLE) {
454             // Returns false if extensions are not available.
455             return false;
456         }
457 
458         return mExtensionsInfo.isExtensionAvailable(baseCameraSelector, mode);
459     }
460 
461     /**
462      * Returns the estimated capture latency range in milliseconds for the target camera and
463      * extension mode.
464      *
465      * <p>This includes the time spent processing the multi-frame capture request along with any
466      * additional time for encoding of the processed buffer in the framework if necessary.
467      *
468      * @param cameraSelector    The {@link CameraSelector} to find a camera which supports the
469      *                          specified extension mode.
470      * @param mode              The extension mode to check.
471      * @return the range of estimated minimal and maximal capture latency in milliseconds.
472      * Returns null if no capture latency info can be provided or if the device doesn't support
473      * the extension mode on this camera.
474      */
getEstimatedCaptureLatencyRange( @onNull CameraSelector cameraSelector, @ExtensionMode.Mode int mode)475     public @Nullable Range<Long> getEstimatedCaptureLatencyRange(
476             @NonNull CameraSelector cameraSelector, @ExtensionMode.Mode int mode) {
477         if (mode == ExtensionMode.NONE
478                 || mExtensionsAvailability != ExtensionsAvailability.LIBRARY_AVAILABLE) {
479             // Returns null for non-Extensions mode or if Extensions are not supported on this
480             // device.
481             return null;
482         }
483 
484         return mExtensionsInfo.getEstimatedCaptureLatencyRange(cameraSelector, mode, null);
485     }
486 
487     /**
488      * Returns whether the given extension mode supports the {@link ImageAnalysis} use case on
489      * the camera specified by the given {@link CameraSelector}. If it returns false, invoking
490      * {@code ProcessCameraProvider.bindToLifecycle} with an {@link ImageAnalysis} use case will
491      * throw an {@link IllegalArgumentException}.
492      *
493      * @param cameraSelector    The {@link CameraSelector} to find a camera which supports the
494      *                          specified extension mode.
495      * @param mode              The extension mode to check.
496      * @return true if {@link ImageAnalysis} can be bound when the specified extension mode is
497      * enabled on the camera specified by the given {@link CameraSelector}. Returns false
498      * otherwise. If the device doesn't support this extension mode on this camera, it will also
499      * return false.
500      */
isImageAnalysisSupported(@onNull CameraSelector cameraSelector, @ExtensionMode.Mode int mode)501     public boolean isImageAnalysisSupported(@NonNull CameraSelector cameraSelector,
502             @ExtensionMode.Mode int mode) {
503         if (mode == ExtensionMode.NONE) {
504             return true;
505         }
506 
507         // Returns false if Extensions are not supported on this device.
508         if (mExtensionsAvailability != ExtensionsAvailability.LIBRARY_AVAILABLE) {
509             return false;
510         }
511 
512         return mExtensionsInfo.isImageAnalysisSupported(cameraSelector, mode);
513     }
514 
515     /**
516      * Retrieves a {@link CameraExtensionsControl} object that allows customization of capture
517      * request settings for supported camera extensions.
518      *
519      * @param cameraControl the camera control for a camera with a specific extension mode turned
520      *                     on.
521      * @return a {@link CameraExtensionsControl} object to manage extension-related settings. Or
522      * returns {@code null} if the provided {@link CameraControl} doesn't represent a camera with
523      * enabled extensions.
524      */
getCameraExtensionsControl( @onNull CameraControl cameraControl)525     public @Nullable CameraExtensionsControl getCameraExtensionsControl(
526             @NonNull CameraControl cameraControl) {
527         return CameraExtensionsControls.from(cameraControl);
528     }
529 
530     /**
531      * Retrieves a {@link CameraExtensionsInfo} object that allows to observe or monitor capture
532      * request settings and results for supported camera extensions.
533      *
534      * <p>If the provided {@link CameraInfo} doesn't represent a camera with enabled extensions, a
535      * placeholder {@link CameraExtensionsInfo} object will be returned, indicating no extension
536      * type and strength support.
537      *
538      * @param cameraInfo the camera info for a camera with a specific extension mode turned on.
539      * @return a {@link CameraExtensionsInfo} object for observing extension-specific capture
540      * request settings and results.
541      */
getCameraExtensionsInfo(@onNull CameraInfo cameraInfo)542     public @NonNull CameraExtensionsInfo getCameraExtensionsInfo(@NonNull CameraInfo cameraInfo) {
543         return CameraExtensionsInfos.from(cameraInfo);
544     }
545 
546     @VisibleForTesting
getExtensionsAvailability()547     @NonNull ExtensionsAvailability getExtensionsAvailability() {
548         return mExtensionsAvailability;
549     }
550     @VisibleForTesting
setVendorExtenderFactory(VendorExtenderFactory vendorExtenderFactory)551     void setVendorExtenderFactory(VendorExtenderFactory vendorExtenderFactory) {
552         mExtensionsInfo.setVendorExtenderFactory(vendorExtenderFactory);
553     }
554 
ExtensionsManager(@onNull ExtensionsAvailability extensionsAvailability, @NonNull CameraProvider cameraProvider, @NonNull Context applicationContext)555     private ExtensionsManager(@NonNull ExtensionsAvailability extensionsAvailability,
556             @NonNull CameraProvider cameraProvider, @NonNull Context applicationContext) {
557         mExtensionsAvailability = extensionsAvailability;
558         mExtensionsInfo = new ExtensionsInfo(cameraProvider, applicationContext);
559     }
560 }
561