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